See it as a diagram
Everything below, as a diagram you can edit. Describe yours and see it in seconds.
No account needed · Editable canvas, not a picture
Four levels, four very different survival rates
Level 1, system context, survives. It is small, it has one box in the middle, and the surrounding actors and systems change on a timescale of years. Most teams draw it once and it stays broadly right.
Level 2, containers, is the level people actually work from and the one that rots fastest. Its subject is deployable units, and deployable units are exactly what changes: a service is added, a job moves to a queue, a database is split. One merge can make the container diagram wrong without anybody noticing, because nothing in the merge touches the diagram.
Level 3, components, decays within a quarter in an active codebase, and it is expensive to draw because internal structure has no boundary that says stop here. Level 4, code, the model itself calls an optional level of detail that is often available on demand from tooling such as an IDE, and it advises against it for long-lived documentation. In practice most teams do not maintain level 3 either, they just do not say so out loud.
The honest reading: budget maintenance for one context diagram and one container diagram per system, and let the lower levels be generated on demand or skipped.
Level 2 is the level worth automating
Containers are deployable units, and deployable units are declared in the repository rather than remembered. Dockerfiles and compose files name the runtimes. Kubernetes and Helm manifests name the workloads and their services. Infrastructure as code names the managed datastores and queues. CI workflows name what gets built and where it ships. Package manifests name the languages and frameworks that fill in a container label.
That list is not a coincidence, it is the priority order a repository read should use. Datadef selects files in exactly that sequence, infrastructure as code first, then containers and orchestration, then data, APIs, CI, and manifests, before ever looking at prose. The read is bounded so the view cannot drown in detail: 40 files, 250KB in total, 30KB per file, and ceilings that under the architecture focus allow 16 infrastructure files and 14 Kubernetes manifests against 3 dbt models. A container-level view is close to what falls out of that selection.
The pieces C4 asks you to write on a container box, the technology and the responsibility, come from those same files. A box labelled orders-api, Go, HTTP, backed by Postgres is assembled from the Dockerfile, the manifest, and the IaC that declares the database.
Where notation purity helps, and where it costs you
The C4 discipline worth keeping is edge labelling. Every relationship gets a verb and a technology, so an arrow says reads orders from over HTTPS rather than pointing vaguely. That single rule removes most of the ambiguity in a system diagram, and it costs nothing to keep in a generated one.
The discipline worth relaxing is the boxes-and-lines-only aesthetic. Strict C4 avoids provider iconography so the diagram stays tool-neutral, which is right for a whiteboard and slow for an audience that already reads cloud icons fluently. A container view with the real managed-service marks on the datastores is read faster by everyone outside the authoring team.
Datadef draws in that middle: zones for boundaries, labelled edges for relationships, and real provider icons on the nodes that have one, from a catalog of more than two thousand marks including 438 AWS, 624 Azure, and 226 Google Cloud. Boundaries are capped rather than open-ended, since a container view stops working long before it stops fitting: no zone holds more than eight nodes, and the end-to-end check that guards generation fails a canvas whose largest zone has grown past ten. The structure is C4-shaped, the reading speed is closer to a cloud diagram.
Structurizr DSL versus a synced diagram
Structurizr is the reference implementation and its model-as-code idea is the right one: describe the model once, render several views from it, keep the source diffable in git. If your team will maintain a DSL file with the same discipline it maintains code, that approach holds up and gives you level 1 and level 2 from one definition.
The cost is a second source of truth. The DSL is a hand-maintained description of a system that already describes itself in its manifests, so it drifts for exactly the same reason a hand-drawn diagram drifts, just with better tooling around the drift. Somebody still has to notice that a new service exists and go edit a file. The Java client library does ship a component finder, and the DSL can call it, but it discovers components by analysing Java types rather than by reading Dockerfiles, Helm charts, or Terraform, which is why the automation on offer lands at level 3 and not at level 2.
A synced diagram removes that step by deriving the container view from the branch on a daily schedule and skipping the work entirely when the head commit has not moved. The trade-off is honest: you get less notational control than a DSL gives you, and you stop maintaining a parallel model.
FAQ
Which C4 levels are worth maintaining?
Why does the container diagram go stale so quickly?
Can a C4 container diagram be generated from a repository?
Is Datadef a Structurizr replacement?
Should C4 diagrams use cloud provider icons?