Read the Design Document Against Itself First

One technical design excluded a channel in §15.8 and advertised support for it in §16, and specified two mutually exclusive mechanisms as both in scope. Self-contradiction needs no environment to find.

Everyone reviews a design document against the requirement. Some people review it against the code.

Almost nobody reviews it against itself.

That is a shame, because a document that contradicts itself needs no environment, no test data and no system access to catch — and it is the most expensive kind of defect to leave in, for a reason that has nothing to do with the document and everything to do with how two teams read it.

Three Contradictions in One Document

A recent technical design, read cover to cover before any test was written.

It excludes a channel and advertises it. Section 15.8 states the telesales channel is out of scope for the release. Section 16 lists "supports telesales and the online journey" as a release benefit.

It specifies two mutually exclusive mechanisms. Sections 7 to 12 have Salesforce resolve the product catalogue from the postcode. Section 15.7 has the external buying journey resolve it instead, and adds that Salesforce will not perform postcode validation for those requests.

Both mechanisms are marked to build. And once you combine them with the exclusion in 15.8, the Salesforce-side mechanism ends up with no consumer in the release at all — it is being built for a channel that has been descoped.

It never decides how matching works. Postcode matching is described three times: "full postcode or prefix," "prefix matching if required," "exact or prefix depending on the agreed business rule." It is never resolved. Every worked example in the document uses a prefix.

Why Hedged Wording Is a Defect, Not Prose

"If required." "Depending on the agreed business rule." "To be confirmed with the business."

These read as neutral, careful, professionally cautious. They are not. They are unassigned decisions, and the build assigns them silently — usually to whatever the examples happen to imply, because a developer with a deadline reads the examples.

In this case, prefix matching. Which has an immediate consequence that the document never mentions: under a "starts with" match, a row for PE1 also captures PE10 through PE19 — and the document's own examples route those to a different catalogue.

That is a customer seeing the wrong price list, derivable from the specification, before a line of code exists.

So treat hedged wording as an open item with an owner and a due date, not as a sentence. And whenever a spec permits prefix matching on any code, ask the same question: what happens to the longer codes that begin with the same characters?

The Deeper One: Granularity

The best defect in that document is not a contradiction at all. It is a mismatch of levels.

The mechanism maps a postcode to a product catalogue. But the property that decides which catalogue applies — whether an address has been overbuilt — is a fact about an individual address, not about a postcode.

Any postcode containing both overbuilt and non-overbuilt properties is therefore guaranteed to show the wrong prices to some customers. Not likely to. Guaranteed, by construction.

The design is internally consistent and still wrong. No amount of correct implementation fixes it, because the lookup key is one level coarser than the fact it encodes. Meanwhile four candidate keys were in circulation across the acceptance criteria, the design and the QA assessment simultaneously — postcode, service ID, an internal reference, and the national address identifier — which is usually the visible symptom of exactly this confusion.

When you review a design, check the granularity of the lookup key against the granularity of the thing it decides. That single check finds defects no test case can, because the system will implement the wrong key perfectly.

Why Two Teams Make It Worse

A contradiction inside one artefact does not stay a documentation problem for long.

Two teams read the same document. Each reads the half relevant to them. Each builds it. Neither is wrong about what the document says, because the document says both things — and the disagreement surfaces at integration, weeks later, as a defect with no obvious owner and a long argument attached.

That is why this is worth doing first, before the build: the fix at document stage is a paragraph. The fix at integration stage is a re-plan.

How To Actually Do It

A self-review pass on a design document, roughly forty minutes:

  • Read it once, end to end, without taking notes. You are looking for the shape of the thing, not for errors yet.
  • Build a list of every named entity — every scope, channel, key, status, role. Then find every place each one appears and check they agree. Naming drift between two sections is the same defect as a contradiction, just quieter.
  • Mark every hedge. "If required," "to be agreed," "depending on." Each becomes an open item with a named owner. If the document has more than three, it is a draft, and saying so is more useful than testing against it.
  • Check the examples against the rules. Examples are where the author's real assumption lives. Where the prose is ambiguous and the examples are consistent, the examples are what will be built.
  • Check the keys against what they decide. One level too coarse is a defect that survives perfect implementation.

None of that requires access to anything. It is the cheapest defect-finding session available to a tester, and it happens before the first build.

When did you last read a design document against itself — and how many sections would still agree if you did?