What does an IDL as the source of truth give a team that a code-first, reflection-derived wire contract does not?
answer
- where does truth live
- contract exists before any implementation
- one file, many generated readers
- review the contract diff, not the class diff
- code-first infers the shape at run time
basics
~20 sAn IDL makes the wire contract an artefact that exists before and apart from any implementation: it is versioned, reviewed and compiled into stubs for every consumer, so no single codebase's types can silently redefine it.
solid answer
~40 sContract-first means the messages are declared in an interface definition language first — named types, fields, field ids — and a generator emits the types and the encode/decode code for each consuming ecosystem. Code-first means the wire shape is derived at run time from one implementation's domain types plus the serializer's configuration; the contract still exists, it is just implicit, and the only complete statement of it is that code. The IDL buys three things: the contract has its own version a consumer can pin, changes are reviewed as a contract diff rather than as a class diff whose blast radius is invisible, and a consumer in another ecosystem generates a reader from the same file instead of reverse-engineering payloads. The cost is a generation step, generated artefacts to keep current, and slower iteration.
go deeper
Recall the plain distinction: contract-first writes a schema file first and generates code from it; code-first writes classes first and lets a serializer work out the wire shape.
Explain the mechanics — what the generator emits, what run-time reflection inspects, and why the review surface differs between a contract diff and a class diff.
Show the operational payoff: pinned contract versions, consuming teams as reviewers, and an honest account of which errors move to build time and which still reach production.
Own the trade-off across teams: when the ceremony of a contract repository is worth it, when a published emitted schema is enough, and who pays the iteration tax.
## The two places a wire contract can live Whenever a value leaves one process and is read by another, some contract governs the bytes: which fields exist, what they are called or numbered, what their types are, and what a reader does with the parts it does not recognise. That contract always exists. The only real question is **where it is written down**. **Contract-first** writes it down first, in an **interface definition language (IDL)** — a small declarative language whose only job is to describe messages: named types, their fields, each field's type, and, in most families, a stable numeric field identifier. A generator (the IDL compiler) reads that file and emits, for every target ecosystem, the types plus the encode and decode code that implement them. Nobody hand-writes an encoder and nobody infers one. **Code-first** writes it down last, or never. A developer declares ordinary domain types in one ecosystem and hands an instance to a general-purpose serializer, which inspects the type at run time — walking its fields, reading whatever declarative markers are attached — and derives a wire shape from what it finds. The contract is a *side effect* of the implementation, and the complete statement of it is that implementation plus the serializer's settings. ## What moves when the source of truth moves | | Contract-first (IDL + generated code) | Code-first (run-time reflection) | |---|---|---| | Where truth lives | one schema file, versioned on its own | one implementation's types, plus serializer settings | | When the shape is fixed | when the contract is compiled | at run time, in whichever build is deployed | | Review surface | a diff of the contract | a diff of a class, whose wire effect is invisible | | A second ecosystem | generates its own reader from the same file | reimplements the shape from source or sampled payloads | | A rename in code | free — the contract is untouched | a wire edit wherever the key follows the identifier | | Cost | a generation step and generated artefacts | almost none, until the first consumer break | ## What the artefact actually buys The frame where the difference is sharpest is a repository that holds nothing but contracts, whose build publishes generated stubs to several consuming teams. Three properties follow: 1. **The contract has a release.** A consumer depends on a named, immutable version, not on "whatever the producer deployed this morning". Two consumers can sit on different versions deliberately, and you can say which. 2. **Some contract errors become build errors.** Only some: a field that disappears from the contract breaks the consumer's compilation, because the generated accessor is gone. A field whose *meaning* changes — cents to a different unit, an identifier that now refers to a different entity — compiles perfectly and fails in production. Contract-first moves a class of errors earlier; it does not move all of them. 3. **Review happens where the blast radius is.** A change arrives as a diff of the contract, in a repository whose reviewers can include the consuming teams. In code-first, the equivalent change arrives as a rename inside a class in the producer's repository, reviewed by people whose attention is on the refactor. ## What it does not buy - It does not tell you **which edits are safe**. An IDL will happily let you delete a field; compatibility rules are a separate discipline layered on top. - It does not enforce **semantics**. Generated types check shape, not invariants, ranges or cross-field rules. - Generated code is only as current as its **last regeneration**, which is its own failure mode. - It does not stop a producer hand-encoding bytes that the contract forbids, if some path bypasses the generated encoder. ## When code-first is the honest choice Code-first is not a beginner's mistake; it is a trade. It fits when one team owns both ends, everything is in one ecosystem, the endpoint is short-lived or internal, and iteration speed dominates. The cost lands later, when a second consumer appears and the only specification is the producer's source. There is also a middle path worth naming, because interviewers like it: stay code-first, but **emit a schema from the types, publish it, pin it and diff it in continuous integration**. Truth still lives in the code, so it is not contract-first, but you recover the review property and the version pin without the generation step. Ecosystems differ widely in how mature that tooling is, which is part of why teams land in different places on the same trade-off.
- Some code-first tooling can emit an IDL file from the implementation types. Does that make the workflow contract-first?Not by itself. The emitted file is downstream: truth still lives in the code, and the file records a decision already made. It becomes contract-first only if the emitted schema is published, pinned to a version, reviewed as the gate, and a mismatch between code and published schema fails the build rather than updating the file.
- What makes a generated stub trustworthy to a consuming team?That it was generated from a named, immutable contract version by a pinned generator version, and that both are stamped on the published artefact. Without that provenance the stub is just code that happened to compile, and a consumer cannot say which contract version its service actually implements.
- Does contract-first require a binary encoding?No. An IDL describes the message, not the bytes; contract families exist for text encodings as well, and some IDLs support more than one encoding of the same contract. Conflating contract-first with compact binary output is a common confusion — the two choices are independent.
A building's dimensioned drawing versus a tape measure taken over the finished wall: both describe the wall, but only one of them exists before the wall, can be checked by someone else, and can be handed to a second contractor.
saying these in an interview costs you the question
- Thinks the IDL is just documentation generated from the code
- Believes code-first services have no wire contract at all
- Says generated stubs make any schema change safe automatically
- Treats the contract file as the producing team's private file
- Assumes contract-first removes the need for validation on decode
- Equates contract-first with choosing a binary encoding