Appendix B - 15. Model-driven codegen
The Structure of Model-driven codegen — its shape at a glance:
A generator reads the model and emits the artifact, stamping a provenance header. A freshness lint re-runs generation and fails when the committed artifact differs, and the provenance lint fails on a hand-edit that dropped the marker.
flowchart LR
M[(Model)] --> Gen[Generator]
Gen --> Art[/Artifact + provenance header/]
Art --> Fresh{{Freshness lint}}
M --> Fresh
Fresh -->|differs| Fail([build blocked])
Accessible description: a generator reads the model and emits an artifact carrying a provenance header. A freshness lint regenerates from the model and blocks the build when the committed artifact differs, so a hand-edit or a stale output is caught.
Projected from the catalogue entry system-models / Model-driven codegen.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Generate real artifacts from the models — NetworkPolicy, service catalog, env wiring, public-API docs, competitor catalog, wire-contract types, docker — each carrying a provenance header, so the model drives the system (not merely describes it) and hand-edits are caught.
Motivation
If the models only described the system, they would be optional — nice docs, easy to ignore, quick to drift. The failure this addresses is a model nothing depends on: nothing breaks if it's wrong, so nothing keeps it right. And separately: a generated artifact that someone hand-edits loses the edit on the next regen, silently.
Applicability
- Models rich enough to generate from (the source-of-truth must contain what the artifact needs).
- Generators that re-emit the provenance marker every run.
- A provenance + freshness lint so hand-edits and stale artifacts are caught.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The generator emits the artifact with a provenance header re-stamped every run. A freshness lint regenerates and fails when the committed file differs — so the model stays authoritative and a stray hand-edit is caught before it is lost.
import sys
HEADER = "# GENERATED from the model — edits are overwritten; change the model instead\n"
def generate(model: list[dict]) -> str:
body = "\n".join(f"allow: {row['name']}" for row in model)
return HEADER + body + "\n" # provenance marker re-emitted every run
def freshness_lint(committed: str, model: list[dict]) -> list[str]:
expected = generate(model)
if committed != expected:
return ["artifact is stale or hand-edited — regenerate from the model"]
if not committed.startswith(HEADER):
return ["provenance header missing — artifact was hand-authored"]
return []
if __name__ == "__main__":
# `load_model` reads the source-of-truth; `read_artifact` reads the committed output file.
findings = freshness_lint(read_artifact(), load_model())
for f in findings:
print(f"STALE-ARTIFACT: {f}")
sys.exit(1 if findings else 0)
Consequences
- A generator per artifact class to author + keep current with the model schema.
- Generated files must not be hand-edited. A real constraint (the provenance lint enforces it).
- Regen discipline. The artifact must be regenerated when the model changes (the freshness lint catches misses).
Example use within DocAble
- The NetworkPolicy, service-catalog, web-API-entity, public-API-doc, competitor-catalog, wire-contract, and docker generators.
- A model-visualization generator (human diagrams, e.g. Mermaid, from the models), so the picture can't drift from the model.
- The provenance-header requirement + its enforcing lint.
Related Patterns
- Enabler — the models (service-flow, domain-registries, deployment-topology) are what it generates from; without them there is nothing to generate.
- Bridge — this is the product-facing face of the models: they don't just inform agents, they build the codebase's config and docs.
- See also (cross-target) — the product provenance & attribution family: provenance headers here, mutation stamps there — both "the tool records what it produced."