Building on the idea that specs are maps — how modern teams use executable specifications, contract testing, and living documentation to align intent with delivery.
Few questions in software development are as deceptively simple as "is it done?" On the surface, the answer should be straightforward — either the work is complete or it is not. In practice, the question exposes one of the most persistent ambiguities in how teams build software. Done according to whom? Done against what definition? Done in the sense that the code was written, or done in the sense that it does what someone, somewhere, actually intended? The gap between these interpretations is where a remarkable amount of software dysfunction lives, and it is a gap that specifications, in their traditional form, have never managed to close.
The trouble begins with the nature of specifications themselves. A specification is an attempt to describe a desired outcome in advance, expressed in language, before the thing it describes exists. This is an inherently difficult act of translation. Intent is fluid, contextual, and full of unstated assumptions; written specification is fixed, literal, and inevitably incomplete. The familiar idea that specifications are maps captures this well: a map is a useful guide to a territory, but it is not the territory, and treating a map as though it were a perfect and permanent description of reality leads predictably to error. The challenge facing modern teams is not to write better maps, but to rethink what role specifications should play in defining done at all.
The Limits of the Traditional Specification
The conventional model of specification treats the written document as the authoritative statement of what should be built. Requirements are gathered, documented, agreed upon, and handed to those who will implement them. The specification becomes a contract in the loosest sense — a record of what was promised — against which the resulting software is eventually judged. This model has been the backbone of software delivery for decades, and its limitations are by now thoroughly familiar to anyone who has worked within it.
The first limitation is that specifications drift out of alignment with reality almost immediately. The moment a specification is written, the understanding it captures begins to age. Implementation reveals ambiguities that the document never anticipated. Stakeholders refine their thinking as they see the software take shape. The environment shifts, priorities change, and the carefully documented requirements become a record of what was once wanted rather than what is now needed. A specification treated as a fixed artifact becomes, over time, a description of a destination the team has long since stopped traveling toward.
The second limitation is more subtle and more damaging: traditional specifications are disconnected from the software they describe. The document lives in one place and the code in another, and nothing enforces any relationship between them. A specification can claim one behavior while the system exhibits another, and there is no mechanism by which this divergence announces itself. The two simply drift apart silently, until someone discovers — usually at an inconvenient moment — that the documented intent and the actual behavior have nothing to do with each other. The specification was a map, but the territory changed and the map was never updated, and nobody noticed because nothing forced them to.
From Description to Contract
The shift that modern teams are making is from thinking about specifications as descriptions to thinking about them as contracts — but contracts in a far more precise and demanding sense than the loose metaphor usually implies. A contract, properly understood, is not merely a record of what was promised. It is an enforceable agreement that defines obligations precisely and provides a mechanism for verifying whether those obligations are met. Applied to software, this reframing changes the nature of specification fundamentally.
A contract in this sense specifies behavior at the boundaries where it matters: what a component promises to provide, what it requires from those who use it, and what guarantees hold when those conditions are satisfied. Unlike a descriptive specification, which narrates intent in prose, a contract defines obligations in terms that can be checked. This precision is not pedantry — it is what allows the contract to do the work that descriptions cannot. When obligations are defined precisely enough to be verified, the question of whether they have been met stops being a matter of interpretation and becomes a matter of fact.
This reframing also redistributes responsibility in a productive way. A descriptive specification places the entire burden of correctness on the implementer, who must interpret the document and hope their interpretation matches the author's intent. A contract distributes that burden more fairly. The provider of a capability is responsible for honoring its side of the agreement, and the consumer is responsible for respecting the conditions under which the agreement holds. The contract makes the relationship explicit, and in doing so it transforms the vague aspiration of "build the right thing" into the concrete and checkable obligation of "honor this agreement."
Executable Specifications: Closing the Gap
The most significant development in this shift is the rise of executable specifications — specifications that are not merely written and read, but run. An executable specification expresses desired behavior in a form that can be checked automatically against the actual system. It is simultaneously a statement of intent and a test of whether that intent has been realized, collapsing the gap between description and verification that traditional specifications could never bridge.
The power of this approach lies in what it makes impossible. When a specification is executable, the silent drift between documented intent and actual behavior can no longer occur unnoticed. If the system stops doing what the specification says it should, the specification fails, loudly and immediately. The map and the territory are bound together; the moment they diverge, the divergence is reported rather than hidden. This is a categorical improvement over a model in which specifications and systems are free to wander apart indefinitely, discovered only when the consequences become painful.
Executable specifications also reshape the definition of done in a way that resolves much of the original ambiguity. When done means "the executable specification passes," the question stops being a matter of opinion. There is a shared, verifiable standard against which completion is measured, and that standard is visible to everyone — those who define intent, those who implement it, and those who depend on the result. This does not eliminate the difficulty of getting the specification right in the first place; defining the correct behavior remains a genuinely hard problem of understanding and communication. But it does eliminate the secondary problem of not knowing whether the agreed behavior was actually delivered, which is a substantial gain in itself.
Contract Testing in Distributed Systems
Nowhere is the contract-oriented approach more valuable than in the distributed systems that dominate modern enterprise architecture. When a system is composed of many independent services, each developed and deployed by different teams, the interfaces between those services become the points where misalignment is most likely and most costly. A change in one service can break the expectations of another, and in a sufficiently complex system, these breakages are difficult to anticipate and harder to trace.
Contract testing addresses this directly by treating the agreements between services as artifacts that can be verified independently. Rather than relying on slow, brittle end-to-end tests that exercise the entire system at once, contract testing verifies that each service honors the specific agreements it has with its collaborators. The provider of a capability confirms that it continues to deliver what it has promised, and the consumer confirms that it depends only on what has actually been agreed. The contract sits between them as the shared definition of correct interaction, and each side can be checked against it separately.
This approach offers a resolution to a problem that has long troubled distributed architectures: how to allow services to evolve independently without constant fear of breaking one another. When the agreements between services are explicit and verified, teams can change their implementations freely as long as they continue to honor their contracts. The contract becomes the stable point around which independent evolution can safely occur, much as a well-designed interface allows the systems on either side of it to change without coordination. The difference is that the contract is not merely declared but continuously verified, which is what makes the independence trustworthy rather than merely hoped for.
Living Documentation and the Single Source of Truth
A recurring failure in software documentation is the divergence between what the documentation claims and what the system actually does. Documentation is written once, the system evolves, and the two drift apart until the documentation becomes actively misleading — a description of a system that no longer exists. This is the same disease that afflicts traditional specifications, and it has the same root cause: documentation that is disconnected from the thing it describes will inevitably fall out of sync with it.
The contract-oriented approach offers a cure in the form of living documentation — documentation generated from, and bound to, the actual behavior of the system. When specifications are executable and contracts are verified, they become a description of the system that cannot be wrong, because they are continuously checked against the system itself. Documentation derived from these artifacts inherits their reliability. It describes not what someone once intended the system to do, but what the system is demonstrably doing right now, verified at the moment the documentation was produced.
This convergence resolves a tension that has run throughout the history of software documentation. The traditional choice was between documentation that was readable but unreliable and verification that was reliable but unreadable — prose that humans could understand but that nothing enforced, or tests that machines could check but that communicated little to a human reader. Living documentation, built on executable specifications and verified contracts, dissolves this trade-off. The same artifact serves as the statement of intent, the verification of behavior, and the description of the system, and because all three are bound together, they cannot contradict one another. The map and the territory become, for the first time, genuinely difficult to pull apart.
Looking Forward
The movement from specifications to contracts reflects a deeper maturation in how the industry thinks about the relationship between intent and delivery. The old model, in which intent was described in prose and delivery was hoped to match it, has always relied on a leap of faith across the gap between the two. The contract-oriented model replaces that leap with a verifiable connection, binding intent and behavior together so tightly that their divergence becomes visible rather than silent. This is not a rejection of the insight that specifications are maps — it is a recognition that maps are only useful when they are kept faithful to the territory, and that fidelity must be enforced rather than assumed.
For teams adopting these practices, the benefits extend well beyond cleaner definitions of done. Executable specifications, contract testing, and living documentation together create systems whose behavior is understood, whose interfaces are trustworthy, and whose documentation can be believed. These are not minor conveniences. They are the foundations on which complex systems can be evolved with confidence rather than fear, by teams that can change their software without first having to rediscover what it was supposed to do.
In the end, rethinking how we define done is not a narrow technical exercise. It is a way of taking intent seriously enough to bind it to reality, and of refusing to accept the silent drift between what we meant to build and what we actually built. The teams that make this shift discover that the question "is it done?" finally has an answer that everyone can trust — not because they have written better maps, but because they have learned to keep the map and the territory in agreement.
- Comments
- Leave a Comment