top of page

Documenting Constraints Without Drowning the Doc

S. West
3 hours ago
7 min read

The vendor shortlist for the claims-triage assistant had three names on it. The one the team preferred, on accuracy in their own evaluation, processed data in the United States. The design document's constraints section settled the question in one line:


All customer data must remain in the EU region (GDPR Article 44).

The vendor was dropped. Four months later, during a procurement review, someone from the privacy office read the line and asked where it had come from. Article 44 of the GDPR doesn't require data to stay in the EU. It requires that transfers to third countries meet the conditions in Chapter V of the regulation, which includes adequacy decisions and contractual safeguards. Whether the organisation wanted to keep claims data in the EU was a legitimate policy question. It had simply never been asked, because the line in the design doc presented a policy choice as a legal fact, and nobody argues with the law.


This example is a composite, built from a pattern common in regulated AI procurement. It is not legal advice: keeping data in the EU may well be the right decision. The point is who made it.


Why constraint sections bloat and still miss the important ones


Constraints sections tend towards two failure modes at once. They grow long: browser support, coding conventions, the cloud provider, the CI tool, the budget, the launch date, every regulation anyone has heard of. And the long list still misses the constraint that bites, because the items that get written down are the ones people are sure of, and the ones that bite are the ones nobody checked.


The underlying problem is that a constraints section mixes four different things under one heading:


What it is

Example

Can it change?

Who decides?

External constraint

A regulation's actual text; a contract clause; a hard deadline set by a regulator

Not by the team

The external party

Organisational decision

"We keep claims data in the EU"; "all services run on our Kubernetes platform"

Yes, by whoever owns it

A named owner

Assumption

"Adjusters have reliable connectivity"; "volumes stay under 10k claims a day"

It might simply be false

Whoever can check it

Preference or folklore

"We're a Java shop"; "we tried microservices once and it didn't work"

Yes, by discussion

The team


All four limit the design. Only the first is truly fixed. Written under one heading with the same weight, the other three borrow the authority of the first.


How the mix causes failures


Assumption → recorded as constraint → design narrows → nobody re-checks → expensive surprise. The claims-triage example is the policy version. The assumption version is more common: "the system must support up to 10,000 records" gets written as a constraint, the design is optimised for it, and nobody owns watching the actual volume. When the business grows past it, the design was correct against a number that was never a limit, only a guess.


The opposite failure is just as costly: a real external constraint gets written as a soft preference and is quietly traded away. "We'd prefer to tokenise card data" is a different sentence from "PCI DSS requires stored card numbers to be rendered unreadable", and only one of them survives a deadline.


Both failures come from the same missing piece of discovery: nobody traced the constraint to its source.


The format: five fields, one of them a source


arc42, a widely used architecture documentation template, gives constraints their own section and recommends simple tables of constraints with explanations, optionally split into technical, organisational and convention constraints. That's the right shape. The field that does the most work is the one most teams leave out: where the constraint comes from, in words specific enough to check.


Field

What goes in it

Claims-triage line, corrected

Constraint

The limit, stated as precisely as the source allows

Claims data is processed and stored in the EU/EEA

Type

External / organisational decision / assumption / preference

Organisational decision

Source

Document, clause, or person, specific enough to verify

Privacy office data-residency policy v3 (2024), section 2.1. GDPR Chapter V permits third-country transfers with safeguards; this policy is stricter by choice

Implication

What it rules in or out for this design

US-only processing vendors excluded unless the policy owner grants an exception

Owner and review

Who can change it, and when it's next checked

Head of Privacy; reviewed annually and at each vendor selection


The corrected line excludes the same vendor. The difference is that the team now knows it is a decision with an owner and an exception route, and the next procurement can ask the owner instead of treating the line as untouchable.


The source test: run it on your current doc


Take the constraints section of a live design document and try this. It takes about thirty minutes for a typical list of fifteen to twenty items.


Source test flow sorting each constraint into assumption, external constraint or organisational decision

  1. For each constraint, write the source. A clause number, a policy name and version, a contract, or a person's name. "Everyone knows" and "compliance" are not sources.

  2. For each external source, read the actual text. Compare what it says with what the constraint says. Flag every line where the constraint is stricter, broader or older than its source.

  3. Re-type everything you couldn't source as an assumption or a preference.

  4. Count. Note how many lines kept the label "constraint" after the source check.


What you're looking for: if more than about a quarter of your list moves out of the "external" type, the section has been carrying authority it didn't have. That's a rule of thumb, not a benchmark, but in practice the first run of this test usually reclassifies more items than the team expects.


The second count matters just as much: assumptions with no owner. Every assumption needs someone whose job it is to notice when it stops being true, and a signal they watch. "Volumes stay under 10k a day; owner: claims ops lead; signal: weekly volume dashboard; review if any week exceeds 7k."


Where each type belongs


Keeping the main document readable is mostly a question of putting each type where it is used.


  • External constraints and organisational decisions go in the constraints section with full fields. These are the ones a reader must not violate without a conversation.

  • Assumptions go in their own short section, or a risk register, each with an owner and a signal. Mixing them into constraints hides the fact that they need watching. The risk register template has the columns.

  • Preferences and conventions go in the team's engineering standards, linked once. They don't need restating in every design.

  • Constraints that shaped a specific decision go in that decision's architecture decision record, in the context section, so the reasoning survives after the doc is out of date.


A design doc that follows this usually ends up with a constraints table of five to ten rows instead of twenty-five, and every row in it can be defended.


Implicit constraints worth surfacing


Some limits are routinely assumed and never written down until they break. Ask about these explicitly during discovery, and record the answer with its type:


  • Data volume and growth. What's the current number, and who said what it would be in two years?

  • Data location and movement, including where AI vendors process prompts and logs.

  • Languages and locales. "English only for now" is a decision with a retrofit cost; give it an owner.

  • Supported clients. Browsers, OS versions, assistive technology.

  • Concurrency. "One user edits a record at a time" is an assumption until someone checks it.

  • Who may change data. Particularly for AI agents with write access. See capturing non-functional requirements for the wider set.


Why this matters more when an AI writes the code


An AI coding assistant knows none of your constraints unless they are in its context. It will happily generate a service that logs full prompts to a US-region bucket, or a schema that assumes a single currency, because nothing in the ticket said otherwise. A short, accurate, sourced constraints table is one of the most useful things you can paste into an assistant's project instructions. A twenty-five-line list with folklore in it is worse than useless there, because the model will treat every line as binding and design around preferences you'd have dropped in a conversation. For the wider pattern, see What AI-Generated Code Structurally Skips.


Keeping the list honest over time


  • Date-stamp the section and review it at each major decision point, not on a calendar alone.

  • Trigger reviews from outside events: a regulation change, a new vendor, a new policy version.

  • Retire constraints visibly. Strike them through with a date and reason rather than deleting them, so readers of older decisions can see what changed.

  • When a design discussion stalls on "we can't, because...", ask for the source line. Half the time the conversation moves straight to the owner, which is where it should have been.


Final takeaway


A constraints section earns its place when every line in it can be traced to a source and an owner. Most sections mix law, policy, guesses and habit, and present all of them with the authority of law. Sorting them by type, and moving the assumptions somewhere they'll be watched, makes the document shorter and the design more open to the options that were never actually ruled out.


Your next action: run the source test on the constraints section of the design you're working on now. Re-type every line you can't source, give every assumption an owner and a signal, and send the reclassified "organisational decisions" to their owners for confirmation.


Sources


Part of the Software Quality Engineering guide — ShiftQuality's complete map to building quality in.

bottom of page