ADR 0020: C4 views, PlantUML conventions, and shared diagram style

Ratification

Adopted under ADR 0018. Link the discussion Issue and merge PR when they exist. If not, use git history as the record.

Context

This section explains what we are standardizing (C4 thinking, PlantUML, shared visuals) and why that matters before the concrete rules in Decision.

Why draw architecture at all?

Software systems live in people’s heads, in code, and in running infrastructure—but those representations answer different questions. Code shows how behavior is implemented; it does not, by itself, give newcomers a stable map of who uses the system, what major parts exist, or where responsibilities sit. Without shared structural pictures, teams rely on oral tradition, which drifts as people rotate and features accumulate. Explicit architecture views reduce misunderstandings in planning, security review, onboarding, and incident response: they are a deliberate compression of complexity, not decoration.

What the C4 model is (and what it is not)

The C4 model (popularized by Simon Brown and described at c4model.com) is a hierarchy of zoom levels for describing software architecture:

  1. Level 1 — System context: the system in its environment—users, external systems, key dependencies. Answers “What is this thing, and who interacts with it from the outside?”
  2. Level 2 — Containers: high-level technical building blocks that could be deployed or replaced separately (applications, databases, message buses, major processes). Answers “What are the main runtime pieces and how do they connect?”
  3. Level 3 — Components: major parts inside a container—layers, modules, services. Answers “How is responsibility divided inside that deployable unit?”
  4. Level 4 — Code (optional in many teams): classes or files. Often left to the codebase and IDEs; we do not require a standing L4 diagram for every feature.

The point is separation of concerns by zoom: each level hides detail that belongs at a finer grain, so diagrams stay readable. Mixing “a user clicks a button” with “this SQL migration file” on one canvas usually produces noise. C4 is not a formal ISO standard with a single mandated notation; it is a mental model and vocabulary that many teams have found compatible with agile delivery and incremental updates.

Why sequence diagrams alongside structure?

C4 views are mostly static: boxes and lines show ownership and dependency. They do not fully capture order of operations across time—who calls whom, validation order, alternate failure paths. Sequence diagrams (UML interaction diagrams) complement structural views by describing message flow for concrete scenarios (for example “create note” or “handle conflict”). Together, structure plus sequences give a fuller picture without replacing tests or code.

Why standardize on one textual notation (PlantUML)?

Diagrams can be drawn in many tools. A textual source (here, PlantUML) aligns with docs as code: the same review, branching, and diff workflows as for application code. Generated images (for example SVG) stay reproducible from source; style can be centralized. Alternatives such as binary drawing files or unconstrained wiki embeds are harder to diff, easier to silently desynchronize from the repository, and harder to lint or regenerate in CI. This ADR does not claim PlantUML is the only good tool in the industry—it claims that one chosen tool for this repo is preferable to many.

Why a shared visual style (colors, fonts, arrows)?

Consistency is not vanity. When every author picks arbitrary colors, readers must re-learn a legend on every page. A small, fixed palette tied to roles (people, APIs, data stores, infrastructure) lowers cognitive load and makes printed or embedded figures look like one publication, not a collage. Shared typography and line weight improve legibility in HTML and when slides are exported from the same sources.

Why this repository cares now

This project already keeps PlantUML under docs/uml/, merges a shared skin from docs/uml/include/style.puml when rendering via Kroki, and references generated SVG from HTML. The gap was a written policy: what C4 means here, how it maps to PlantUML shapes, and how to evolve the skin without one-off diagrams drifting away. This ADR turns an informal habit into an explicit contract so future contributors know what “good” looks like and why.

Decision

All architecture and interaction diagrams that live under docs/uml/ are authored in PlantUML using standard UML and structural elements. We describe systems using the C4 model as the mental map (levels 1–3), implemented with PlantUML primitives—not as a mandate to use the third-party C4-PlantUML macro library, which would complicate single-file Kroki rendering. Visual consistency comes from the shared file docs/uml/include/style.puml, injected at render time by scripts/regenerate_docs.py.

Scope of notation (UML / PlantUML only)

C4 levels mapped to PlantUML elements

Follow C4 naming for titles and intent; map elements as below so colors from the shared skin apply predictably.

C4 level → suggested PlantUML shapes
C4 level Intent PlantUML
L1 System context People and external systems around the product actor for people; package to group external apps; rectangle for the system under design; database when a store is iconic at this zoom level
L2 Containers Deployable apps, data stores, major technical building blocks node for a process or host boundary; [Container] rectangles inside; database for stores; package for ancillary stacks (for example observability)
L3 Components Major parts inside a container (layers, modules) package per layer or bounded context; [Name] components inside; data access via database / repositories as in existing examples

Reference implementations: docs/uml/architecture/system_context_view.puml, container_view.puml, system_component_view.puml.

Titles, labels, and arrows

Sequence diagrams

Density, legends, and splitting

Canonical visual template (shared skin)

The file docs/uml/include/style.puml is the single source of truth for fonts, corner radius, arrow weight, and fill colors. scripts/regenerate_docs.py inserts it immediately after @startuml when sending a diagram to Kroki. Do not duplicate those skinparam lines in each diagram.

Palette and typography (must stay aligned with style.puml)
Role Setting Values (hex)
Page / packages Package background and border Background #F1F5F9, border #CBD5E1
Software / API / components Rectangle (default component) Fill #F8FAFC, border #2563EB
Data stores Database Fill #FFF7ED, border #C2410C
Infrastructure / deployment Node Fill #F8FAFC, border #64748B
People Actor Fill #ECFDF5, border #047857
Arrows Generalization / dependency lines Color #64748B, thickness 1.1

To render a diagram without the shared skin (rare debugging), put !NO_STYLE on the line immediately after @startuml; the merge script strips the marker and skips injection.

File layout and outputs

Regenerate: make docs-fix or .venv/bin/python scripts/regenerate_docs.py. Verify CI: make uml-check or scripts/regenerate_docs.py --check.

Scope

Alternatives considered

  1. C4-PlantUML library
    • Pros: explicit C4 stereotypes and legends.
    • Cons: typically relies on !include; Kroki does not expand includes in our single-file upload path without bundling—would require a local preprocessor or checked-in expanded files.
  2. Mermaid in Markdown
    • Pros: readable in GitHub without render step.
    • Cons: second notation and styling path; weaker alignment with existing PlantUML-based docs pipeline.
  3. Manual edits to rendered images
    • Pros: pixel-perfect control.
    • Cons: not reproducible; violates docs-as-code for diagrams.

Consequences

Positive

Trade-offs

Compatibility and migration

Implementation plan

  1. Keep style.puml and regenerate_docs.py in sync; any new global skinparam belongs in the shared file.
  2. When adding a diagram, copy structure from an existing architecture or sequence file; run make docs-fix and commit sources plus SVGs.
  3. Cross-link this ADR from engineering or system-design docs when those pages are updated.

Validation

References

Page history

Date Change Author
Added Page history section (repository baseline). Ivan Boyarkin