skip to content

Schema-Driven Output

Turning an external description such as a data schema, an interface definition or a grammar into compiled source, keeping one source of truth. Interviewers ask how drift from hand edits is prevented.

on this pageshow

questions

5

Where should a hand-written addition to a generated telemetry type live, given the file is rewritten on every build?

level: middleimportance: must knowfreq 62%

answer

  1. the file has an owner
  2. output, not source
  3. regeneration overwrites, it never merges
  4. leave a seam for hand code
  5. shared need belongs in the description

basics

~20 s

In a hand-owned file the generator never writes, attached through an extension seam the generated type exposes: a type that extends it, a hook it calls, or a wrapper around it. Generated files are build output, not source.

solid answer

~40 s

Once message shapes are written once in an external description and generated into source, the generated files stop being source and become **build output**. Nothing reads them back, so a hand edit is discarded by the next run that writes the file — there is no merge step and no comment marker the generator is obliged to honour. The addition belongs in a hand-owned file joined to the generated one through a deliberate seam: a hand-written type that extends the generated one, an operation the generated code calls whose implementation is hand-written, or a wrapper that holds the extra behaviour and delegates the generated parts. If several consumers need the same addition, it is not an extension at all — put it in the description so every output carries it.

code

pseudocode · 10 lines
pseudocode
// emitted from the description - rewritten on every generator run
type GeneratedReading extends nothing:
    field deviceId
    field celsius
    field capturedAt

// hand-owned file, never written by the generator
type Reading extends GeneratedReading:
    function isSuspect():
        return celsius < -90 or celsius > 200

go deeper

for a junior

Remember the rule and the reason behind it: a file produced from a description is output, so an edit to it has nowhere to be read back from and disappears when the file is produced again.

for a middle

Explain the mechanics: generation composes the file from the description alone, there is no merge step, and the seam — an extending type, a called hook, or a wrapper — is what gives hand-written code a home that survives.

for a senior

Show how you make the habit impossible in practice: regeneration in the ordinary build so an illegitimate edit dies within a day, an obvious seam per generated type, and a rule for when a need moves into the description instead.

for a principal

Weigh the seam as a published contract. A generator with no extension point forces every local need through a shared description and turns it into a union of special cases; too many hooks make generated code a framework you now maintain.

## Generated source is an output, not an input When the message shapes of a device-telemetry pipeline are written down once, in an external **description**, and a generator turns that description into source for every consumer, the emitted files change role. They are no longer source in the sense a hand-written file is source: they are **build output that happens to be readable**. The description is the input; the files are what the build makes of it, before compilation, on the way to a binary. Every rule about editing them follows from that single fact. A hand edit inside a generated file is an edit to an output. Nothing in the pipeline reads it back. Any run that writes the file composes it from the description alone, and whatever was typed there is gone. There is no three-way merge, and a comment asking the generator to preserve a region is only honoured if that generator was built to parse its own previous output and reason about which parts a human meant to keep — a much harder program than the one most teams have. ## Why the loss is quiet The failure mode is not that the edit is rejected. It is that the edit **works**, until it silently does not: - The edit compiles and passes tests locally, so nothing at the moment of the change objects. - An incremental generator may skip a description that has not changed, so the file survives for days and the habit spreads to other files. - When the file is finally rewritten, the diff is full of churn — reordered members, regenerated headers — and the one deletion hides inside it. - The tests that fail are the tests for the hand-added behaviour, which point at the generator rather than at the habit that caused it. So the answer an interviewer wants is not "do not edit generated code" as a slogan. It is a **place for the addition to live** that survives regeneration by construction. ## The extension seam A generator that expects hand-written additions leaves a seam. Three shapes cover nearly everything: 1. **Extension by subtype.** The generator emits a type meant to be extended; a hand-owned type extends it and adds members. The generated file is rewritten freely; the hand-owned file is never touched. 2. **A hook the generated code calls.** The generated code invokes a named operation at a defined point, and the implementation of that operation is hand-written and discovered by declaration or registration. This is the only seam that lets hand-written code influence generated *behaviour* rather than merely adding beside it. 3. **Composition.** The generated type stays a plain data carrier; a hand-written wrapper holds the extra behaviour and delegates the generated parts. | Seam | What the generator emits | What the human writes | Main cost | |---|---|---|---| | Subtype | An extensible type | A type extending it | Hand code is coupled to the generated shape | | Hook | A call to a named operation | The implementation | The generator must have anticipated the point | | Wrapper | A plain carrier | A wrapper that delegates | Two types to keep in step in every consumer | ## When the addition belongs in the description instead The seam is for what is genuinely local to one consumer. Ask who needs the addition: - **Several consumers need the same thing** — that is a missing feature of the description. Add it once and every output gets it; otherwise the same helper is hand-written three times and the single source of truth quietly becomes four. - **Exactly one consumer needs it** — the seam is right. Widening the description for one consumer turns it into a union of everybody's special cases, and each one is generated into outputs that do not want it. - **The addition must change generated behaviour, not add to it** — the seam has to be an inversion point, where generated code delegates the decision to the hook. Without one you are back to editing output, and the only honest options are to extend the description or to fork the generator. ## The limits worth naming A seam is a design commitment made by whoever wrote the generator, and a generator with no seam forces every local need through the description. Extension by subtype couples hand-written code to the generated shape, so a description change can break the hand-written side at build time — which is usually the outcome you want, because it is loud and early rather than a silent behaviour change. And none of this removes the need for regeneration to be part of the ordinary build: a seam only helps if the generated file is genuinely rewritten often enough that an illegitimate edit dies quickly rather than months later.

  • The addition must change what the generated code already does, not just add beside it. Does the seam still work?
    Only if the seam is an inversion point: the generated code calls a named operation and lets the hand-written implementation decide. Adding a member beside generated code cannot override generated behaviour. If no such hook exists, the honest choices are to extend the description so the generator emits the behaviour, or to change the generator — not to edit its output.
  • Two other consumers turn out to need the same hand-written helper. What changes?
    It stops being an extension. A need shared by several outputs is a missing feature of the description: express it once there and let every consumer receive it. Leaving it as three hand-written copies re-creates exactly the duplication the single description was adopted to remove, and the copies drift independently.
  • Why not have the generator preserve regions marked as hand-written?
    Some generators do, but it makes the generator parse and understand its own previous output, and the preserved region can then reference members the new description no longer emits. The result is a file with two owners and a merge policy nobody wrote down. A seam in a separate file gives the same benefit with no merge at all.

The generated file is a printout of a spreadsheet: writing a number on the paper changes nothing in the sheet, and the next printout has no memory of it.

saying these in an interview costs you the question

  • Says hand edits are fine if you leave a comment asking the generator not to touch them
  • Assumes the generator merges hand edits back into its next output
  • Reformats generated files by hand to match house style
  • Freezes the generator version so the file stops being rewritten
  • Believes a version-control conflict will catch the lost edit
  • Copies the generated file and edits the copy, abandoning the description
open as a page

Should source generated from your telemetry description be committed to the repository, or produced by every build?

level: middleimportance: should knowfreq 50%

basics

~20 s

Regenerating on every build keeps the description the only source of truth but makes the generator a build dependency. Committing the output removes that dependency and makes changes reviewable, at the price of drift nothing detects. A common middle path regenerates and fails the build on any difference.

open as a page

Which changes to a telemetry description break a generated consumer's build, and which regenerate silently?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Changes to the shape a consumer references — a renamed or removed member, a changed arity, a member that becomes required — break compilation for every consumer regenerated from the new description. Changes to meaning, units, defaults or documentation regenerate into code that still compiles, so nobody is told.

open as a page

Three teams each hand-maintain their own telemetry message types; what do you weigh before making one generated description mandatory?

level: principalimportance: should knowfreq 36%

basics

~20 s

Weigh who owns and reviews the description, whether the generator is maintained infrastructure or a weekend script, what happens the day one bad description change breaks three builds at once, whether the notation can express what the odd team needs, and how you would walk it back.

open as a page

Why derive a device's session state machine from a declarative transition table instead of hand-writing the transitions?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

Because a transition table is data that can be checked before any code exists: unreachable states, missing or duplicated transitions and dead ends are found by walking the table. Hand-written transitions hide the same defects in control flow, and drift from the diagram everyone reasons about.

open as a page