The C4 Model Explained: Context, Container, Component, Code
Ask five engineers to draw "the architecture" of a system and you'll get five different pictures, at five different levels of detail, none of them wrong exactly, but none of them talking to each other either. One shows boxes for every microservice. Another shows a single cloud icon and an arrow to "the internet." A third is a UML class diagram nobody has opened since the sprint it was drawn in. The C4 model exists to fix that specific mess — not by declaring one of those pictures correct, but by giving each one a defined place in a sequence of zoom levels that all describe the same system.
C4 was created by Simon Brown, a software architect who got tired of watching teams either not diagram their systems at all, or diagram them once, badly, at a single arbitrary level of detail that satisfied nobody. He published the approach through his book Software Architecture for Developers and the c4model.com reference site, and it has since become one of the more widely adopted lightweight notations for describing software architecture — not because it's clever, but because it's disciplined about a problem most notations ignore: which level of detail are you even looking at, and who is it for.
The problem C4 is actually solving
Before C4, most teams that bothered to diagram their architecture at all tended to produce one of two things. Either a single, overloaded diagram trying to show the whole system — every service, every database, every queue, every class — which becomes unreadable the moment the system has more than a handful of moving parts. Or a scattered pile of diagrams with no relationship to each other: a PowerPoint slide from a stakeholder meeting, a whiteboard photo from a design session, a UML diagram generated once by an IDE and never regenerated. Each diagram is internally consistent. None of them agree on what a box means, what level of detail is appropriate, or how they relate to one another.
C4 solves this with a genuinely simple idea: instead of one diagram, or an unstructured pile of diagrams, you produce a small, ordered set of views — Context, Container, Component, and optionally Code — each one a deliberate zoom level into the same system. Simon Brown's own analogy is a map application. Zoomed all the way out, you see a country and its borders. Zoom in and you see a city. Zoom further and you see streets. Zoom further still and you see individual buildings. Nobody would argue that the city-level map and the street-level map are "different maps that disagree" — they're the same territory, shown at a resolution appropriate to the question you're currently asking. C4 applies that logic to software architecture diagrams.
One system, four zoom levels — not four diagrams
This is the part that gets lost when teams adopt C4 as a checklist rather than as a principle. The four levels are not four separate deliverables you produce once and file away. They are the same system, described at increasing magnification, for audiences who need different amounts of detail to do their job. A Context diagram and a Container diagram for the same system should never contradict each other — the software system that's a single box in the Context diagram is the same software system whose insides get expanded in the Container diagram. If a container in your Container diagram represents something that isn't part of the system shown in your Context diagram, something has gone wrong, not because of a diagramming rule, but because you've described two different systems and called them the same one.
That consistency requirement is exactly why C4 pairs so naturally with a model where architecture is stored as data rather than as independent pictures — a point we'll come back to. For now, the practical implication is simpler: before drawing anything, decide what the system boundary actually is, because every other level depends on it staying fixed.
| Level | What it answers | Who it's for |
|---|---|---|
| 1 — System Context | What is this system, and who and what does it interact with? | Everyone — technical and non-technical stakeholders, new hires, auditors, executives |
| 2 — Container | What is the high-level shape of the system — what actually runs, and how do the pieces talk to each other? | Technical staff: developers, operations, architects across teams |
| 3 — Component | Inside one container, what are the major structural building blocks, and how do they collaborate? | The developers who build and maintain that specific container |
| 4 — Code | Inside one component, how are the classes, interfaces, and functions actually structured? | The developer currently working in that code, usually via IDE tooling rather than a hand-drawn diagram |
Read top to bottom, each level answers a narrower question for a smaller, more technical audience. Read bottom to top, each level is an abstraction over the one below it — a container is an abstraction over the components and code inside it; a system in a Context diagram is an abstraction over the containers inside it. Nothing is lost by zooming out; detail is simply not shown, because the audience at that level doesn't need it to answer their question.
Level 1: System Context
The System Context diagram is the widest shot. It shows the system you're describing as a single box — usually labelled with what it does, not how it's built — surrounded by the people who use it and the other software systems it depends on or is depended on by. That's the entire scope. No internal structure, no technology choices, no databases. If someone asks "what does this diagram tell me about how the system is deployed," the honest answer at this level is: nothing, and that's correct, because that's not the question a Context diagram is trying to answer.
The audience is deliberately broad. A System Context diagram should be readable by a product owner, a new engineer on day one, an auditor who has never seen the codebase, or an executive who wants to understand what a system does without wading into technical detail. That constraint shapes what belongs on it: real people (or personas — "Citizen," "Case Officer," not "User"), the system itself as one box, and the external systems it genuinely talks to — a payment provider, an identity system, a national registry, whatever is actually true for that system. Nothing about the internal split into services, nothing about the database engine, nothing about deployment topology.
Take a permit management platform as a worked example — a citizen-facing service that lets people apply for building or environmental permits, tracks the application through review, and takes payment for the associated fee. At the Context level, the diagram needs exactly three kinds of things: the citizen who applies, the case officer who reviews, the system itself, and the external payment provider it calls out to during checkout. That's it. Whether the permit platform internally has three services or thirty is invisible at this level, and should be.
In Mooodels, this level is just a view over the model filtered to one software system, its people, and its direct external relationships — nothing about it is a separately maintained artifact. Define Citizen and Case Officer as people, the permit platform and the payment provider as software systems with the second one marked external, and the three relationships between them — submits applications, reviews and decides, processes fees over HTTPS — and the Context view is one query away. Nothing is drawn twice, so there is no second copy of the permit platform that can quietly stop agreeing with the first.
Level 2: Container
This is the level where C4's vocabulary trips people up, because "container" does not mean Docker container, Kubernetes pod, or any specific packaging technology. In C4, a container is a separately runnable or deployable unit — a web application, a single-page app running in the browser, a backend API, a mobile app, a database, a message broker, a batch process. The test is not "does this run in a container image," it's "does this run and get deployed on its own, independently of the other boxes on the diagram." A PostgreSQL database is a container in this sense even though nobody ships it as an application artifact — it's a separate runtime thing with its own lifecycle.
Zooming into the Permit Platform box from the Context diagram, the Container diagram shows what that box is actually made of: a citizen-facing web portal, a backend API that owns the application workflow, and a database. It might also show a separate case-management service if that logic runs and deploys independently, or a message queue if the workflow is asynchronous. What it does not show is internal code structure — no classes, no packages, no controller/service/repository split. That detail belongs one level down.
The same underlying elements from the Context diagram still apply — the system boundary drawn there is the dashed outline here — but now the audience has changed. This diagram is for the people who build, deploy, and operate the system: which service owns the database, what protocol two services actually talk over, where a new service would need to be inserted. Expanding into this level means giving the platform an inside: a React portal for citizens, a Node.js service called PermitAPI that owns the application workflow, a Java service called CaseWorkflow that handles review and decisions, and a PostgreSQL database, PermitDB. The relationships between them each carry the protocol they actually run over — HTTPS from the portal to PermitAPI and from PermitAPI to CaseWorkflow, SQL over TCP from PermitAPI to the database. The software system defined at Context level isn't redrawn to make room for any of this; it simply gains contents.
A Container diagram is usually the single most useful C4 level for day-to-day engineering conversations — it's detailed enough to plan a change against, and abstract enough that it doesn't need updating every time someone renames an internal class.
Level 3: Component
Zoom in once more, into one specific container, and you get the Component diagram. This shows the major structural building blocks inside that container and how they collaborate — not classes, but the coarser groupings that classes live inside: a controller layer that handles inbound requests, a workflow engine that drives approval logic, a gateway that talks to the external payment provider, a repository layer that talks to the database. Components are usually not separately deployable on their own — they're distinguishable, cohesive chunks of the container's internals, often mapping fairly directly onto packages, namespaces, or modules in the actual codebase, but they don't have to map one-to-one with any specific code structure.
This is also the level where C4 itself is the most explicit about being optional in practice. Simon Brown's own guidance is that Component diagrams are useful when a container's internal structure is genuinely non-obvious or worth documenting deliberately — a service with real architectural decisions baked into its internal shape — and considerably less useful for a container that's simple enough that the component breakdown would just restate the container's job in smaller boxes. Drawing one for every container out of habit tends to produce diagrams nobody reads and nobody keeps current.
Zooming into PermitAPI from the container example above, a reasonable Component diagram might show four components: an application controller that receives and validates permit submissions, a workflow engine that drives the approval process, a payment gateway that talks to the external payment provider, and a repository that reads and writes permit records. The controller hands work to the workflow engine, and the workflow engine in turn calls both the gateway and the repository. That's the whole internal shape — four cohesive chunks and three collaborations, at a level of detail a developer joining the service would actually find useful.
Notice what changed and what didn't: PermitAPI itself doesn't reappear as a box — it's the boundary the whole diagram lives inside, the way PermitPlatform was the boundary for the Container diagram. Each level takes the previous level's box and opens it up, without redrawing the system from scratch.
Level 4: Code
The fourth level goes inside a single component to show class or interface-level detail — essentially a UML class diagram, or something close to it, for the small piece of code that implements one component. C4's own guidance here is the most conservative of all four levels: this is rarely worth hand-drawing, and it's the level most likely to be stale the moment it's committed, because class structure inside a live codebase changes constantly during normal development. Simon Brown recommends generating this level from the code itself, on demand, using IDE tooling — most modern IDEs can produce a UML diagram from a package or class selection in seconds — rather than maintaining it as a standalone artifact anyone owns.
The practical rule of thumb that's emerged among teams using C4 seriously: Context and Container diagrams are worth maintaining deliberately, because they change slowly and are read by many different people over time. Component diagrams are worth drawing selectively, for containers whose internal structure genuinely needs explaining. Code diagrams are worth generating on demand, when someone specifically needs that level of detail in front of them, and not worth maintaining as a persistent document at all.
What C4 deliberately doesn't specify
A detail that surprises people coming from heavier enterprise notations: C4 doesn't mandate a specific visual notation, icon set, or tool. Brown's own guidance is close to "boxes, lines, labels, and a consistent legend" — what matters is that a reader can tell, at a glance, whether a given box is a person, a software system, a container, or a component, and that the same shape means the same thing everywhere in the diagram. Plenty of tooling has grown up around C4 with its own opinionated visual style, but the model itself is a way of thinking about abstraction levels, not a stencil library.
C4 also defines a small set of supplementary diagram types that sit alongside the four core levels rather than replacing them, for situations the core four don't quite cover: a System Landscape diagram for showing several systems at once rather than one system's neighborhood, a Dynamic diagram for showing the order of interactions in a specific scenario rather than static structure, and a Deployment diagram for mapping containers onto actual infrastructure — which servers, which regions, which environments. These are useful in the right circumstance, but they're additions to the core four, not a fifth mandatory level everyone needs.
Where teams get C4 wrong
The most common failure mode isn't drawing the wrong things — it's treating the four levels as four independent documents rather than one system described four ways. That shows up as a Container diagram that quietly implies a different system boundary than the Context diagram above it, or a Component diagram for a container that was renamed three sprints ago in the Container diagram but never updated here. Each individual diagram might look fine in isolation. What's broken is the thing C4 is actually for: the guarantee that zooming in and out describes one consistent reality.
A second common mistake is over-investing in the lower levels relative to how often anyone reads them. It's tempting to produce a beautiful, exhaustive Component diagram for every container and a Code diagram for every component, because doing so feels thorough. In practice, those diagrams are the most expensive to keep accurate and the least frequently consulted — most people asking architectural questions are asking Context or Container-level questions ("what talks to what," "what would this change affect"), not Code-level ones. Spending disproportionate effort on the levels people read least is a reliable way to end up with stale diagrams and a discouraged team that stops updating any of them.
A third is skipping straight to Container or Component because Context feels "too obvious to bother drawing." It's usually the cheapest diagram to produce and the one with the widest audience — the one an auditor, a new hire, or a stakeholder in a budget meeting can actually read without translation. Skipping it doesn't save much drawing effort and removes the one artifact that non-engineers can use unassisted.
C4 in a model-native tool
The discipline C4 asks for — one system, several honest zoom levels, no drift between them — is exactly the kind of thing that's hard to sustain by hand across four separately maintained diagrams, and much easier to sustain when the levels are views over one underlying model rather than four artifacts. That's the shape of the C4 profile in Mooodels: Person, Software System, Container, and Component are element types in one canonical model, and the four diagrams aren't drawn four times — they're four views, each filtered to a different scope and level, over elements that only ever exist once.
Concretely, that means the container you defined inside PermitPlatform is the same object whether you're looking at it in the Container view or clicking into it from a Component view — there's no second copy of "PermitAPI" that can silently drift out of sync with the first, which is exactly the failure mode described above. Rename a container, and every view that shows it updates, because they were never independent pictures to begin with. Add a new component inside PermitAPI, and the Container-level box for PermitAPI doesn't need to be touched at all — it's still one box, correctly, because the Component diagram is a deeper view into it, not a separate document that needs to stay manually in sync.
That same model also travels reasonably well if you're arriving from Archi or Sparx EA with existing ArchiMate models — the C4 profile sits alongside the ArchiMate one in Mooodels rather than forcing a rebuild, so a software system or application component already modelled there can be a starting point for a Container-level breakdown rather than a reason to start over. And because AI assistance in Mooodels proposes changes as a reviewable patch rather than editing directly, asking an AI assistant to "sketch a Container diagram for the new case-management service" produces a set of proposed containers and relationships you approve or adjust — not containers silently added to a model you didn't get to review first.
Deciding how far to go
Not every system needs all four levels, and treating "four levels" as a mandatory checklist for every project is its own way of getting C4 wrong. A small internal tool with two people using it and one container running it might reasonably stop at Context and Container — there's no meaningfully separate internal structure worth a Component diagram, and Code-level detail is a five-second IDE action away if anyone ever needs it. A large, business-critical platform with a dozen services, several of which have genuinely intricate internal logic, is exactly where Component diagrams for the two or three trickiest containers earn their cost, while the simpler containers around them don't need one at all.
The judgment call C4 asks architects to make isn't "did you produce all four diagrams" — it's "does the level of detail on this diagram match what the audience reading it actually needs to know, and does it agree with the levels above and below it." Get that right, and the four levels stop being paperwork and start being what they were designed to be: a way to answer "what is this system, and how does it work" honestly, at whatever resolution the question was actually asked at.
See the model this article describes, working in a real editor.
Try the live demo