The C4 model in practice
Separate system context from container detail, use a copyable YAML example, and review the diagram for responsibilities the schema cannot check.
Suppose a payments diagram puts a customer next to a retry worker and a database table. A reviewer has to infer which boxes are whole systems and which are implementation details before they can follow a payment.
Choose the diagram's scope before adding more boxes. The useful part of C4 is deciding what belongs in the current view and what needs a separate diagram.
Start outside the system
A system context diagram shows the system being discussed, the people who use it and directly connected external systems. Technology detail can wait. A reader should be able to identify what the system does and who depends on it.
For this hypothetical example, the system is a payments platform. Start with a customer placing an order. In a drawing tool, that means a person connected to a system with an arrow labelled by the action. Keep individual services out of this view.
Here is a complete YAML version. It extends the Customer node in our DSL examples with a system and a relationship. The IDs are local diagram identifiers, not deployed resource names.
apiVersion: archdocs/v1
title: Payments context
layout:
direction: right
nodes:
- id: customer
type: c4-node
kind: person
label: Customer
description: Places orders and tracks deliveries
- id: payments
type: c4-node
kind: system
label: Payments Platform
description: Accepts payments for orders
relationships:
- from: customer
to: payments
label: Pays for an order
This example deliberately leaves the payment provider unspecified. Before using it as a real context diagram, add the provider your system actually calls and label what crosses that boundary. Don't add an external system just because another company's payment diagram has one.
Open the system boundary in a separate view
The container diagram opens the system and shows applications and data stores, their responsibilities and how they communicate. A C4 container means an application or data store; it doesn't imply a Docker container. Deployment topology belongs in a deployment view.
The next template uses the Payments Platform boundary and child nodes from the DSL reference. It adds responsibilities and a labelled relationship. This is still a hypothetical system, with no claim that it models a production payment stack.
apiVersion: archdocs/v1
title: Payments containers
layout:
direction: right
nodes:
- id: payments
type: c4-boundary
label: Payments Platform
children:
- id: api
type: c4-node
kind: service
label: Payments API
technology: NestJS
description: Accepts payment requests and records their outcome
- id: ledger
type: c4-node
kind: database
label: Ledger DB
technology: PostgreSQL
description: Stores payment records
relationships:
- from: api
to: ledger
label: Writes payment records using SQL
The relationship uses the child IDs directly. The boundary groups them visually. In a whiteboard version, draw the same boundary around the API and database, and keep the relationship attached to those boxes.
This small diagram leaves questions open. Is the ledger the source used for reconciliation? Who can write to it? Does the API call a payment provider before or after storing a record? Add details only when you can answer them from the system you're documenting. If the ordering affects correctness, use a sequence diagram alongside this view.
Stop before documenting every class
A component diagram opens a container and describes the components inside it. C4's own guidance makes this view optional. Use it when a reader needs to understand an internal responsibility split that the container view cannot explain.
For example, an API with separate request handling and reconciliation logic might warrant a component view during a redesign. A diagram that copies the directory tree adds another file to update without necessarily explaining anything. Link to code when code is the more accurate view.
Keep the reason for a decision alongside the picture. If payment records use PostgreSQL because reconciliation needs relational queries, record the actual reasoning in an architecture decision record. The technology label alone can't preserve that discussion.
Review the meaning after checking the syntax
For either template, ask a reviewer to trace a named operation through the diagram. Check that every arrow has a direction and describes an action. A label such as uses leaves the reviewer guessing whether a service reads a database, writes to it or calls an API in front of it.
Then check scope. A database table belongs in an ERD, while a database can belong in the container view. Moving a detailed box into a separate view is often a better fix than making the overview larger.
Archdocs compiles these YAML documents into canvas elements, but the schema doesn't enforce C4 levels. It accepts service and database node kinds in the same document as person. The scope rules still need a reviewer. There's also a layout constraint: documents containing c4-boundary can't use explicit pins such as at, or freeform shape, text and raw nodes. Let that document auto-layout, or use plain c4-node cards for a hand-positioned view. The rest of our architecture as code writing covers the trade-offs between editable source and a maintained model.
Architecture docs that stay alive
ArchDocs keeps diagrams and docs in sync with your systems instead of letting them rot. Create a free account to start documenting your architecture.