Skip to content

The interface changed and its contract document did not

A contract maintained by hand beside the code it describes goes stale on the first change that forgets it.

→ shipped an interface change whose contract document still described the old one

cost when it happens
high
how often
common

The moment

A response type gains a field. The implementation is correct, the tests pass, and the published specification still describes last month's shape — so every consumer generated from it is wrong in a way that will surface in somebody else's codebase.

Why it happens

The specification is a second, hand-maintained representation of something the code already states precisely. Two representations of one fact require an act of synchronisation, and that act is performed by memory. It is skipped the first time somebody is in a hurry, and after that the document is untrustworthy, so people stop reading it, so it stops being updated at all.

Why a better model does not fix it

A worker asked to change the implementation changes the implementation. Asking it to also update the specification is asking for the synchronisation to be performed by something else that can forget; the fix is to have one representation.

What it costs

Broken consumers, whose breakage is discovered by the consumer rather than by the producer. And the slow loss of the document itself: once a specification has been wrong twice, it is treated as documentation rather than as a contract.

What Majordomus does

An operation is declared once, as a typed descriptor beside its implementation, with its input and output types. Everything external is derived from that declaration: the MCP tool, the HTTP route, the OpenAPI operation with its schemas and examples, the browsable reference, the benchmark target and the generated documentation. Adding an operation by editing a transport registry or a specification file is not possible, because those are outputs. generate --check names every generated file that differs from what the declaration produces, and CI refuses the tree.

Before and after

before   handler.rs (new field)  +  openapi.yaml (hand-written, unchanged)

after    $ majordomus generate --check
         stale: docs/generated/openapi.json (differs)
                [reproduce: majordomus generate]

What it does not do

It does not review an interface design, and it does not decide whether a change is breaking. It removes the possibility of the description and the behaviour being maintained separately.

What this looks like

Concrete situations, one per audience. Each is declared in the moment's front matter, so the before and the after are data rather than prose a page could drift from.

  1. The specification written twice platform-team

    before
    A field is added to a response type and the specification is not updated, so every generated client is wrong.
    after
    The specification is derived from the same typed declaration the implementation uses, and CI refuses a tree in which it is stale.
  2. Consumers generated from a stale document open-source-maintainer

    before
    Downstream clients are generated from a published contract that drifted two releases ago.
    after
    The published document is a build output of the declaration; there is no second place for it to drift from.
  3. A boundary that was reviewed once enterprise

    before
    The reviewed interface document and the served interface are two artefacts with no enforced relationship.
    after
    One declaration, many projections; the reviewed artefact is regenerated and diffed rather than trusted.

How you would know

The observable symptoms this moment declares. They are the questionnaire on the index and the input of majordomus why diagnose; nothing else defines them.

  • An interface specification is maintained by hand alongside the code that implements it. hand-written-spec
  • A published contract describes a version of the interface that no longer exists. spec-behind
  • Adding an operation means editing both the implementation and a separate registry or document. two-places-to-edit

Where this lives in the tool

Everything below is read out of this moment's own front matter and resolved against the repository. A name here that did not exist would fail validation.

the commands that answer it

the capabilities of the executable that answer it

the claims that back this page, and the evidence behind each

the rules that govern it

  • project.interfaces-are-projections
  • project.rust-canonical-declaration
  • project.derived-files-regenerated
  • majordomus.projection-integrity

the use cases that show the way out

If this one is familiar, so is the next

What this moment names, what names it, and what shares its area, audience or tags. The second and third are derived; only the first is written down.

All 38, and how they connect to the tool →