skip to content

questions

6

What does an IDL as the source of truth give a team that a code-first, reflection-derived wire contract does not?

level: middleimportance: must knowfreq 68%

answer

  1. where does truth live
  2. contract exists before any implementation
  3. one file, many generated readers
  4. review the contract diff, not the class diff
  5. code-first infers the shape at run time

basics

~20 s

An 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 s

Contract-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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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
open as a page

A team renames a field on a domain type during a refactor and consumers break — why did a code-first serializer let that happen?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Because the serializer derives the wire key from the identifier at run time, the rename edited the contract. Nothing in the producer's build knows the difference between an internal refactor and a wire change, so no gate fired.

open as a page

Which constructs would you keep out of a shared IDL contract that several ecosystems generate readers from, and why?

level: middleimportance: should knowfreq 42%

basics

~10 s

Keep out anything whose meaning is borrowed from one ecosystem's type system: runtime-typed polymorphism, deep or recursive nesting, collections whose ordering or key rules differ, and names that collide with a target's reserved words.

open as a page

In a shared contract repository, how can generated client stubs drift from the contract version they claim to implement?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Drift enters wherever generation is not reproducible from a pinned input: stubs built from a working copy instead of a tagged contract, a different generator version, hand-edited generated files, or an artefact whose stamped version is not the one it was built from.

open as a page

Would you publish prebuilt generated stubs from a contract repository, or have each consuming team generate from the IDL at build time?

level: principalimportance: should knowfreq 33%

basics

~20 s

It is a trade between uniformity and autonomy. Publishing stubs centralises the generator version and spares consumers a toolchain, at the cost of the contract repository owning a build pipeline per ecosystem; consumer-side generation reverses both.

open as a page

Which parts of a wire contract can an IDL's generated types enforce, and which must still be checked by hand?

level: middleimportance: nice to knowfreq 26%

basics

~10 s

Generated types enforce shape: declared fields, declared types, and that undeclared fields cannot be set through the generated surface. Ranges, cross-field rules, units, referential validity and state legality are invariants no generator checks.

open as a page