Context & scope — the boundary and what crosses it

What is inside your boundary, what is outside, and what crosses it. arc42 asks for the diagram; the harder questions are what each exchange costs you and what you do when the system on the other side lies.

Spine concern · arc42 §3 · spine.yaml: context

In one paragraph

Context is the boundary: what you can change and release, what you cannot, and what crosses between them. For every external system, record what you exchange, who initiates, what the exchange costs and what happens when it returns something wrong. Then write what is explicitly out of scope. An external system you have not written down is one an agent will invent an integration for.


Lens 01

The Basics

Durable. What this concern is responsible for, regardless of decade or stack.

This is the cheapest concern to draw and the most commonly drawn badly. A context diagram with nine boxes and eleven arrows looks like documentation and answers almost nothing an engineer or an agent needs before writing the call. The boxes are the easy part; the columns beside them are the artefact.

The boundary is what you can change

Inside is what you can modify, deploy and roll back on your own cycle. Everything else is context. Not ownership, not network location, not whether the same company pays for both — the test is whether you can change its contract and release the change yourself.

Which means another team's service in the same cluster is external. You cannot alter its interface, you cannot roll it back, and you learn about its incidents roughly the way its other consumers do. Teams that draw the boundary around the company instead of around the deploy unit end up with a context diagram that omits the dependencies most likely to page them.

Scope follows the same rule and adds one thing: it is defined as much by what you refuse as by what you build. A boundary that has never rejected a responsibility has not been drawn yet.

Four questions per external system

What crosses — data, events, files, money. Who initiates, because inbound and outbound fail in different ways and need different defences. What the exchange costs you in latency, money and coupling. And what happens when it lies.

Most context diagrams answer the first and imply the second. The third is what makes a design argument possible: an exchange that adds 300ms to a synchronous path or a per-call fee to every request is a fact the strategy has to respond to, and it is invisible on a diagram.

The fourth is the one that turns into an incident. Not "when it is down" — that case is obvious and usually handled — but when it returns stale data, wrong data, a 200 with an empty body, or a timeout that your client records as a success. Write the answer per system, because it differs: some exchanges should fail the request, some should degrade, some should queue, and a few can be ignored entirely.

Out of scope is half the artefact

List the systems you deliberately do not integrate with, the responsibilities you are not taking, and the data you deliberately do not hold. Three lines, and they prevent the most expensive category of late surprise.

The cost of leaving it out is a sentence everyone has heard: we assumed you were doing that. A stakeholder's mental model expands to fill any space the spec leaves empty, and the discovery usually arrives during integration testing, when the boundary is already load-bearing.

Not holding data is also an achievement, not an omission. A system that has never stored a card number or a national identifier has a smaller compliance surface for that reason alone — record it, or someone adds a convenient column and nobody notices the boundary moved.

Trust belongs to each crossing, not to the perimeter

Anything you cannot deploy is at best semi-trusted, wherever it sits on the network. Trust granted by location is how one malformed upstream payload becomes your data-corruption incident: the validation was skipped because the caller was inside.

So validate at the boundary, in one place, and keep their model out of yours. An anti-corruption layer is not ceremony — it is what stops an upstream's field names, enum values and idea of a customer from spreading through your domain and making their next breaking change your migration.

A context entry is complete when

It names who initiates
Inbound and outbound are different designs. Inbound needs validation and rate limits; outbound needs timeouts and a failure policy.
It says what crosses
In the terms a residency or retention rule would use, so the constraint that applies is obvious rather than inferred.
It prices the exchange
Latency, money, coupling. An unpriced dependency cannot be argued with when it turns out to sit on the critical path.
It answers what happens when it lies
Fail, degrade, queue or ignore. Decided once, in the spec, rather than improvised in each call site.
It has one interface
The single place in the repo allowed to call it. This is what makes the entry enforceable rather than advisory.
It cites its constraints
Residency, retention, contractual response times. The exchange is where those obligations actually bite.

In the spec

What the concern looks like once it is written down — the machine layer entries a build check or a coding agent resolves by id, and the prose beside them.

arch/spine.yaml — the fields something acts on
context:
  - id: EXT-1
    system: Payments provider
    direction: outbound
    protocol: https/rest
    owner: external vendor (acct mgr in vendors/README)
    trust: untrusted
    on_failure: fail          # no silent success on a payment
    interface: src/adapters/payments
    data: order totals, card tokens; never raw card numbers
    constrained_by: [C-1]

  - id: EXT-2
    system: Ledger service (platform team)
    direction: outbound
    protocol: grpc
    owner: platform-accounting
    trust: semi-trusted
    on_failure: queue         # outbox, reconciled hourly
    interface: src/adapters/ledger
    data: posted transactions

  - id: EXT-3
    system: Model provider (summaries)
    direction: outbound
    protocol: https/rest
    owner: external vendor
    trust: untrusted          # its output is input to us
    on_failure: degrade       # hide the summary, keep the page
    interface: src/adapters/llm
    data: order descriptions, no personal data
    constrained_by: [C-1]

  - id: EXT-4
    system: Partner reconciliation drop
    direction: inbound
    protocol: sftp
    owner: partner-ops
    trust: untrusted
    on_failure: ignore        # yesterday's file is not urgent
    interface: src/inbound/partner-files
arch/05-context.md — the four columns and the refusals
# 5. Context and scope

| System | What we exchange | What it costs us | When it lies |
|---|---|---|---|
| EXT-1 Payments | totals, card tokens | ~180ms p99 in checkout; per-txn fee | Fail the order. A payment we cannot confirm is not a sale. |
| EXT-2 Ledger | posted transactions | async; couples our schema to theirs | Queue via outbox. Duplicate posts are idempotent on txn id. |
| EXT-3 Model | order text -> summary | ~1.4s p95; per-call | Drop the summary. Never let generated text reach a query. |
| EXT-4 Partner file | daily CSV | none on the request path | Skip the file, alert. Two missed days is an incident. |

Observed availability, last quarter: EXT-1 99.93 (published 99.99),
EXT-2 99.8, EXT-3 99.5. EXT-1 and EXT-3 are the ceiling on QG-1.

**Explicitly out of scope**

- We do not store card numbers. Tokens only, ever. (Keeps EXT-1's
  compliance surface out of this system — see C-1.)
- We do not own tax calculation. The partner does. Asked three times.
- No direct integration with the CRM. Reporting reads the warehouse.

Lens 02

The Current

Reviewed 2026-09-09

How it actually plays out in production now. Dated, because this is the part that decays.

The boundary has moved. A large share of any current system is bought rather than built — identity, payments, email, search, inference, flags, observability — and each of those is an availability ceiling and a cost line you do not control. The dependencies teams most often leave off the diagram, though, are the internal ones.

Composite availability is the number that bites

Every synchronous dependency on the critical path multiplies into your own availability. Three vendors at 99.9% in a request path cap that path near 99.7% before your code contributes a single failure, and no amount of internal engineering recovers it. That arithmetic belongs next to the quality goal it constrains.

Record both numbers per system: the availability the vendor publishes and the one you observe. They differ, and the observed number is the one your users experience. The gap is also the strongest argument you will ever have for moving a dependency off the synchronous path.

Internal dependencies deserve the same row

Another team's service, the database two teams still share, the platform's ingress, the internal identity service. These get left out because they feel like us rather than them, and they behave exactly like third parties: independent release cycles, their own incidents, contracts you cannot change unilaterally.

Write them down with the owning team named. The name is the field that gets used — during an incident, and during the six weeks before a breaking change, when the only thing standing between you and a surprise is knowing who to ask.

Correlated failure hides one level down

Three independent providers, one shared identity provider underneath. Two regions, one control plane. A queue and a database in the same availability zone. Redundancy on a diagram is not redundancy in a failure, and the shared upstream is invisible at your boundary.

Note it where you know it. You will not map every transitive dependency, and you do not need to — the value is in the two or three cases where the thing you called redundant has a single point of failure one hop down.

The topology is generated now; the inventory still is not

Traces, service maps and dependency graphs come free with any competent observability stack, and they are better than a hand-drawn diagram because they cannot be out of date. Let the tooling own the picture.

What no tool infers is the part that matters: what the exchange costs, what the contract obliges, whether the data may leave a region, who to call, and what to do when the response is wrong. Generate the graph, and spend the time you saved on the four columns.


Lens 03

Future-ready

What changes when agents write and operate the code. Opinionated on purpose.

An agent with no context list does not stop and ask. It invents — a plausible endpoint, a plausible SDK, a retry loop nobody sanctioned — and it does so confidently, because a hallucinated integration compiles and its unit tests, written against the same invention, pass.

The context list is an allowlist

The invented endpoint is the visible failure and the least expensive one. The costly version is subtler: the agent picks the wrong one of two real systems, or calls the vendor SDK directly from the domain when an adapter already exists three directories away. Both look correct in review.

An enumerated list — ids, systems, direction, and the one interface path each is reached through — makes the right choice the obvious one, and it is small enough to sit in every session rather than be fetched when someone remembers. The agent is also good at maintaining it: finding calls that bypass an interface, systems present in code but absent from the list, entries with no failure policy.

The interface field is what becomes a guardrail

One declared path per external system, and every call to it lives there. That is a rule a build can check — an import boundary test, a lint rule, a dependency-cruiser config — which turns a line of prose into enforcement.

This is the same move the guardrails concern makes generally, and it pays double here. It stops the vendor SDK spreading into four modules, and it gives an agent a single, unambiguous answer to "where does this call go", which is the question it otherwise answers by inventing a location.

Failure policy is not guessable

Timeouts, retries, idempotency and degradation are architecture, not code style. Absent a stated policy an agent writes something plausible, and plausible defaults are where the outages live: a retry loop on a non-idempotent write, an infinite backoff on a user-facing path, a swallowed error that records an empty response as truth.

One enum per entry — fail, degrade, queue, ignore — removes the guess. It is four characters of spec that decide whether a generated client is safe, and it is the field a reviewer can check without holding the whole system in their head.

Model providers are context entries with strange properties

A model provider is an external system like any other, and it breaks the columns in unfamiliar ways: non-deterministic responses, per-call pricing that scales with usage rather than with capacity, versions that change underneath you, and latency measured in seconds.

The trust field is the one that matters most and is most often skipped. Text returned by a model, flowing into a tool call, a query or a template, is untrusted input crossing your boundary — whatever the arrow on the diagram suggests about direction. Treat the model's output the way you would treat a request body from the public internet, and record it as such, because an agent reading the spec will otherwise reasonably conclude that a system your own code called is a system your own code can believe.


How this concern fails

Named, because a failure mode you can name is one you can spot in your own repository before it costs you a quarter.

The box-and-arrow diagram

Nine systems, eleven arrows, no columns. It looks like documentation and tells the person writing the client nothing about cost, contract or failure — which is everything they needed.

The internal dependency left off

Another team's service is treated as part of us until its incident becomes yours. It has its own release cycle and its own contract; the only thing missing was the row.

No out-of-scope list

Scope expands by assumption. Somebody expected reconciliation, or notifications, or tax; it surfaces during integration, when the boundary is already load-bearing.

Trust granted by network location

Inside the perimeter, so validation is skipped. One malformed upstream payload later, the incident is a data-corruption cleanup rather than a rejected request.

The undeclared second caller

The vendor SDK imported in three modules because no interface was declared. The day their contract changes, three teams migrate instead of one adapter.

The happy-path-only dependency

What happens when it lies was never answered, so a timeout is recorded as a success and an empty body is written down as truth. This is the failure mode that becomes a postmortem.


Go deeper


Frequently asked

What belongs in a system context diagram?

The system in the middle, every external system it exchanges anything with, and the direction of each exchange. That is the diagram. The useful artefact adds four columns the diagram cannot carry: what crosses, what the exchange costs in latency and money, who owns the other side, and what your system does when the response is late, empty or wrong. Diagrams answer who; the columns answer what to write in the client.

What is the difference between context and scope?

Context is what exists around your system; scope is which of it you are responsible for. They are recorded together because the gap between them is where late surprises live. Practically, context is the list of external systems and exchanges, and scope is that list plus an explicit set of refusals — the integrations you are not building, the responsibilities you are not taking, the data you deliberately do not hold.

Is another team's internal service an external system?

Yes. The boundary is drawn around what you can change and release, not around the company. If you cannot alter its interface or roll back its deploy, it behaves like a third party: its incidents are your incidents, its breaking changes are your migrations, and it needs the same row in the table, with the owning team named. Internal dependencies are the ones most often missing from a context diagram and most often involved in an incident.

How does this map to arc42 section 3?

It is arc42 section 3 with the columns filled in. arc42 asks for business and technical context — the external systems, the interfaces, the protocols — and stops there, which is reasonable for a documentation template. The Spine keeps that content and requires the cost of each exchange, a failure policy, a trust level, and one declared interface path per system, then gives every entry an EXT- id in spine.yaml so a decision, a constraint or a guardrail can cite the exchange it applies to.

Is this the same as a C4 context diagram?

The C4 context level is a good notation for the picture, and there is no reason to replace it. Keep it, and generate it from traces if your tooling can. The Spine's context concern is the inventory beside the picture: the fields a check or a coding agent acts on, which no notation carries and no observability tool can infer.

Why do AI coding agents need a context list?

Because without one they invent integrations. An agent asked to add a call to a payments provider will produce a plausible endpoint, a plausible client and a retry loop it chose for you, and all of it compiles. An enumerated list — system, direction, the one interface path it may be reached through, and what to do when it fails — replaces four guesses with four facts, and the interface path can be enforced by a build check rather than trusted.

How do you keep a context list current?

Tie it to two triggers rather than a calendar. Any pull request adding an outbound call or an inbound endpoint touches the list, which is a review habit and, where an interface path is declared, a check. Then, on the same quarterly review as the quality goals, compare the list against the dependency graph your observability stack already produces — anything in the graph and not in the list is either a missing entry or a call that should not exist.


The other eleven concerns

Pages are being written one at a time. The ones without a page yet are still in the template, with prompts instead of prose.

Context & scope

Take the template, not the idea

The zip, the single-file variant, the schema and the agent bundle. Nothing behind an email address.

Free to fork, modify and use commercially. No attribution required.