Table of Contents
A model endpoint can be healthy while the feature around it is broken.
A valid model response says nothing about whether the application used authorized data, checked current business facts, or completed a requested state change.
The model does not own most of the feature’s contract. Application code must enforce account scope, choose the right systems of record, validate hard constraints, and report whether state changes succeeded.
A dependency register adds behavioral contracts to the interactions shown in an architecture diagram.
Start with enforceable properties
“Recommend a suitable guitar from our current catalog” is a useful product promise, but it is not precise enough for an operational contract.
Translate it into properties the application can enforce:
- Every recommended product ID resolves against the accepted catalog version used for the request.
- Every displayed price comes from the designated pricing source and is no older than the allowed maximum when the response is produced.
- The feature says “in stock” only when the inventory observation is recent enough for that claim.
- Every customer record matches the authenticated account scope before the application uses it.
- The response reports a successful save only after the shortlist write is confirmed.
Not every part of the product promise becomes a runtime invariant. Product existence, account scope, freshness, and save outcomes can be enforced deterministically. Whether a recommendation is genuinely useful or subjectively suitable remains an evaluation problem. It needs quality criteria, representative test cases, and feedback; the application cannot generally prove subjective suitability for each request.
Freshness limits and time budgets belong to the product and its service objectives. Name them, assign an owner, and enforce them.
For the guitar assistant, the request path might look like this:
authenticated shopper
-> resolve account scope
-> authorize profile access
-> read preferences and optional purchase history when enabled
-> resolve the accepted catalog version
-> verify that the version is still acceptable
-> read product records and specifications
-> read current price observations
-> read inventory observations when availability matters
-> resolve prompt, schema, route, deployment, and model versions
-> call the model
-> validate schema and selected product references
-> resolve authoritative prices and availability when relevant
-> revalidate budget and applicable availability constraints
-> compose authoritative facts into the response
-> write a shortlist when requested
-> return recommendation and save outcome
observability: signals across the complete path
Catalog-version selection and request-time freshness validation are separate because they fail differently.
For exact business facts, the model can rank product IDs while application code resolves price and relevant availability from their systems of record. If the final price exceeds the budget or required availability no longer satisfies the request, reject that candidate. Use another validated candidate, make one bounded regeneration when the contract allows it, or state that the product-backed request cannot be completed.
The register applies established reliability thinking to any distributed feature. AI makes it especially useful because a successful model call can still produce an invalid result. Generated references, probabilistic quality, token and quota limits, content filters, and fallback compatibility are especially easy to miss when teams rely mainly on service diagrams.
Separate three dependency roles
Not every dependency participates in the immediate response. I separate three roles before deciding how critical an interaction is.
| Role | Purpose | Guitar assistant example |
|---|---|---|
| Execution | Produces or validates the result or a requested state change | Account authorization, price read, model request, shortlist write |
| Prerequisite | Creates usable state before requests can consume it | Catalog ingestion and accepted-version publication |
| Operational | Detects, supports, audits, or recovers the feature | Telemetry export, security-event delivery, recovery scheduler |
An operational dependency may not affect one response, yet remain required for audit, billing, safety, or recovery. Analytics may be best effort; required security-event delivery is not.
Classify interactions, not services
A capability rarely has one relationship to the request: catalog publication happens beforehand, version validation inline, and recovery later. Give each identifiable interaction a row with its role, condition, criticality and horizon, timing, and contract reference. Split it again at another owned boundary or operating lifecycle; keep different failure classes of one call inside its failure contract.
Timing does not imply importance. Criticality has three useful values:
- Required: The declared capability, claim, or operational obligation cannot be fulfilled without the interaction.
- Degradable: The system may omit or reduce the affected capability or operational behavior under an explicitly defined degraded mode. Any user-visible limitation must be made clear.
- Best effort: Failure invalidates neither the user-visible result nor any required operational obligation, although it may reduce analytical or operational value.
Criticality describes whether the affected capability or operational obligation may be omitted under the stated condition. It never relaxes security, integrity, authorization, or correctness constraints when an interaction is attempted. “Live inventory is required” is too broad. “A current inventory observation is required for this request when the response claims an item is in stock” is operationally useful.
For the guitar recommendation, an initial classification could look like this:
| Interaction | Role | Condition | Criticality and horizon | Timing | Contracts and details |
|---|---|---|---|---|---|
| Resolve and enforce account scope | Execution | Stored customer data is read | Required for that read | Inline | Profile access policy |
| Read purchase history | Execution | Optional purchase-history personalization is enabled | Degradable for this recommendation | Inline | Profile read · feature detail |
| Publish an accepted catalog version | Prerequisite | Product-backed recommendations remain available | Required over the freshness window | Pre-request | Catalog publication |
| Verify accepted catalog version | Execution | Product-backed recommendation | Required for this request | Inline | Catalog acceptance |
| Read product record and specifications | Execution | Response recommends a catalog product | Required for this request | Inline | Catalog read |
| Read authoritative price | Execution | Response displays a price or enforces a budget | Required for that claim | Inline | Pricing read |
| Read inventory | Execution | Response makes an availability claim | Required for that claim | Inline | Inventory read |
| Resolve compatible prompt and output-schema versions | Execution | Model-generated recommendation | Required for this request | Inline | Prompt and schema contract |
| Resolve model route and version | Execution | Model-generated recommendation | Required for this request | Inline | Model routing |
| Call the model deployment | Execution | Model-generated recommendation | Required for this request | Inline | Model gateway |
| Validate schema and product references | Execution | Model-generated recommendation | Required for this request | Inline | Output contract |
| Revalidate budget and applicable availability constraints | Execution | Response enforces a budget or makes an availability claim | Required for that constraint or claim | Inline | Recommendation constraints |
| Attempt shortlist save | Execution | Shopper asks to save | Required for the save-success claim | Inline | Shortlist write · feature detail |
| Discover eligible unresolved shortlist operations | Operational | Inline attempt lease expired and operation remains non-terminal | Required over the recovery objective | Deferred | Recovery discovery |
| Resume or reconcile non-terminal shortlist operation | Execution | Eligible operation remains unresolved | Required until terminal, when promised by the persistence contract | Deferred | Shortlist recovery · feature detail |
| Export recommendation analytics | Operational | Product analytics is enabled | Best effort unless another obligation requires it | Deferred | Analytics export |
In a working register, the last cell links the shared contract and any feature detail. An explicit promise to use stored purchase history makes that read required unless the request supplies the same information.
Do not combine catalog, pricing, and inventory reads when they are separate calls or owned contracts. Conversely, DNS failure, throttling, content filtering, and malformed output may be separate failure classes of one model call rather than four register entries.
Stop at an owned boundary
Stop at an observable boundary with a documented owner and support contract. Internals stay outside the register unless your application interacts with them separately or they have a distinct operating lifecycle.
Expand only risky interactions
The classification table is the feature register. Expand rows that handle protected data, change state, return ambiguous outcomes, permit degradation, or need a distinct operational response. Other rows can reference a shared contract.
| Artifact | Owns |
|---|---|
| Feature register | Interaction-specific condition, criticality, time horizon, and degraded user behavior |
| Shared capability contract | API or schema version, owner, general retry semantics, SLO, and compatibility policy |
| Runbook or threat model | Operational response, escalation details, and trust-boundary analysis |
Store shared facts once; feature entries contain only operation-specific behavior and overrides.
Use this expanded template only where the risk warrants it:
### <Interaction name>
- Feature register row: <link>
- Capability contract: <link and version>
- Data classification: <organization's classification>
- Freshness or completion requirement: <maximum age or completion target>
- Per-attempt and total time budget: <attempt and end-to-end allocation>
- Failure behavior: <response by material failure class>
- Retry override: <feature-specific difference>
- Degraded mode: <valid reduced behavior, or none>
- Consistency and idempotency: <read or write guarantees>
- Concurrency control: <lease claims and conditional transitions>
- Diagnostic signals: <outcome, duration, attempt, controlled identifiers>
- Escalation path: <owner or runbook>
Read authorized purchase history
- Feature register row: Execution, inline, and degradable when optional purchase-history personalization is enabled; authorization remains required
- Capability contract: Versioned profile-read contract with enforced authenticated account scope
- Data classification: Organization's protected-customer-data class
- Freshness or completion requirement: No feature-specific freshness override
- Per-attempt and total time budget: All attempts fit inside the request's profile-read allocation
- Failure behavior:
- Unavailable or timed out: continue without purchase-history personalization
- Authorization denied: do not read; continue only if an unpersonalized result is valid
- Scope or cache-partition mismatch: suppress the result and start the security incident path
- Malformed response: reject the data and record a contract failure
- Retry override: Do not retry authorization denials, scope mismatches, malformed data, or after caller cancellation
- Degraded mode: Omit personalization without claiming otherwise. Suspected cross-account exposure has no degraded mode
- Consistency and idempotency: Use account-partitioned cache keys and preserve account scope through every call
- Diagnostic signals: Outcome, duration, attempt, authorization reference, and controlled scope identifier; exclude purchase-history contents
- Escalation path: Profile runbook for availability; security incident runbook for suspected cross-account access
This distinction is easy to miss: purchase-history personalization is degradable, but correct authorization is not. Missing data can produce a valid reduced result. Data from the wrong account invalidates the entire operation and may indicate that protected data was exposed.
Attempt shortlist save
- Feature register row: Execution, inline, and required for the save-success claim when the shopper asks to save
- Capability contract: Versioned idempotent write state machine with status lookup by operation ID
- Command durability: `PendingDispatch` stores the immutable save command, or a durable reference sufficient to reconstruct and verify it, plus its fingerprint, operation ID, idempotency key, attempt lease, and `nextAttemptAt`
- Command identity: Reusing the operation ID or idempotency key with a different command fingerprint is rejected
- Data classification: Organization's applicable customer-data class
- Freshness or completion requirement: Report success only after the write is confirmed
- Per-attempt and total time budget: Write and inline lookup share the request's save allocation
- Failure behavior:
- Confirmed success: report that the shortlist was saved
- Confirmed rejection: return the recommendation with a separate save failure
- Timeout or lost response: mark `OutcomeUnknown` and query while inline time remains; if still unknown, return the operation ID with an unconfirmed save status
- Retry override: After an unknown outcome, retry only with the same idempotency key when safe deduplication is guaranteed
- Degraded mode: The recommendation may still succeed, but the save result remains separate
- Consistency and idempotency: One operation ID, idempotency key, and command fingerprint identify the logical save
- Concurrency control: Dispatch requires an atomic conditional update that acquires the lease and transitions the eligible state to `Submitting` using the expected version. Terminal updates require the expected version and attempt identity so an older attempt cannot overwrite a newer result
- Diagnostic signals: Operation ID, outcome, duration, and attempt; exclude shortlist contents
- Escalation path: Persistence errors use the write runbook; the recovery scheduler discovers eligible unresolved operations
A confirmed rejection and a timeout with an unknown outcome are different states. The inline interaction ends with the response; unresolved recovery does not.
Resume or reconcile non-terminal shortlist operations
- Feature register row: Execution, deferred, and required until the operation reaches a terminal state when the persistence contract promises recovery
- Capability contract: State machine, authoritative status lookup, and idempotent retry contract for the original operation ID
- Data classification: Same class as the original save
- Freshness or completion requirement: Reach a terminal outcome within the recovery objective
- Per-attempt and total time budget: Scheduled attempts use the recovery budget, not the request budget
- Recovery eligibility: The inline attempt lease has expired and `nextAttemptAt` is due
- Concurrency control: The worker atomically acquires the lease and transitions the expected state and version before acting. Terminal updates require the active attempt identity and expected version
- Recovery behavior:
- `PendingDispatch`: dispatch the stored command with its existing operation ID and idempotency key
- `Submitting` or `OutcomeUnknown`: perform authoritative lookup or a safe idempotent retry of the stored command when permitted by the shared contract
- Confirmed saved: record the terminal success
- Confirmed rejected, or authoritatively absent after the contract's visibility window: record the terminal failure
- Non-authoritative absence, unknown, or unavailable: reschedule within the recovery policy; escalate when its deadline is exhausted
- Retry override: Reuse the original operation ID, idempotency key, and command fingerprint; never create a new logical save
- Degraded mode: None for saving; the returned recommendation remains valid
- Consistency and idempotency: Lookup, retry, and recovery refer to one logical operation
- Result publication: Persist the terminal status so the shopper and support tooling can resolve the original operation ID
- Diagnostic signals: Operation ID, outcome class, attempt, age, and terminal result; exclude shortlist contents
- Escalation path: Exhausted recovery uses the persistence-reconciliation runbook
Recovery resolves the requested operation to a terminal outcome; it does not always complete the state change. It is deferred execution work. The discovery scheduler is operational: it scans eligible unresolved operations and exposes recovery lag. The feature’s operating or admission policy decides whether a breached recovery objective stops new saves, changes save behavior, restricts traffic, or triggers operator intervention.
Fallback routes and cross-region or cross-provider attempts also consume the same remaining budget unless the feature contract explicitly defines another execution model.
Preserve the contract when degrading
Another model is not automatically a safe fallback. It may behave differently with structured output, context limits, tools, latency, or safety controls. An older catalog version may restore access while violating the freshness limit. Skipping a failed validation step is not graceful degradation.
For a general comparison, valid degradation may omit optional purchase-history personalization or unverified availability claims. When the request explicitly requires products that are available now, return a clearly identified partial result or state that the availability-backed request cannot be completed. A recommendation may still return with a separate save failure.
The feature may return less, but it must not silently change what success means.
Use existing artifacts as inputs
The register synthesizes existing decisions. Diagrams identify interactions and trust boundaries; failure analysis supplies failure classes; SLOs supply budgets; threat models and runbooks supply controls and escalation paths.
Register entries can also inform trace design. Use stable interaction names and record duration, outcome, retry count, and controlled correlation identifiers. Do not log prompts, retrieved documents, purchase history, or tool arguments indiscriminately. Observability has its own access and retention boundaries.
Apply it one operation at a time
Start with one user-visible operation:
- Separate enforceable properties from quality criteria.
- Map and classify the interactions; split distinct boundaries and lifecycles.
- Link shared contracts and expand only risky rows.
- Test slow, stale, malformed, unauthorized, duplicated, unavailable, and unknown-outcome paths.
Use it for protected data, retrieval, tools, state changes, authoritative claims, or operational obligations. A disposable local spike may need only basic error handling.
A useful dependency register determines runtime behavior before an incident rather than merely explaining the incident afterward. A successful model response does not override a violated account boundary, stale price, unverified availability claim, or unconfirmed write.
Related reading
- Calling a Model Is Easy. Running an AI System Is Not
- Observability for Agents with OpenTelemetry, Aspire, and Application Insights
Further reading
The dependency-register structure in this article is a synthesis. These references provide the broader failure-analysis, AI application-design, testing, retry, asynchronous-processing, and telemetry guidance behind it:
- Microsoft Learn: Architecture strategies for performing failure mode analysis
- Microsoft Learn: Application design for AI workloads on Azure
- Microsoft Learn: Test and evaluate AI workloads on Azure
- Microsoft Learn: Retry pattern
- AWS Builders’ Library: Making retries safe with idempotent APIs
- Microsoft Learn: Asynchronous Request-Reply pattern
- OpenTelemetry: Generative AI semantic conventions