c4-diagrams · v0.1 · 2026-05-01 · sha256 fcfe702fbaace845

c4-diagrams v0.1A

Immutable. This exact content never changes and is served at /api/v1/blob/fcfe702fbaace845.

---
name: c4-diagrams
description: Use for architectural analysis and documentation; create C4 PlantUML diagrams with clear actors, system boundaries, containers, components, and relationships.
license: Apache-2.0
metadata:
  author: Stephan
  version: "0.1"
allowed-tools:
  - scripts/render.sh
  - scripts/build_explorer.sh
---

# C4 Diagrams with PlantUML

## When to use this skill

Use this skill when you need to:
- Create C4 context/container/component/code diagrams in PlantUML
- Capture actors, systems, containers, and their relationships for an architecture view
- Ask for or refine inputs like primary actors, system boundaries, key containers, and data flows
- Maintain diagrams in `$REPO_ROOT/docs/diagrams` by default. You may ask the
  user if she wants to have a `./diagrams/` folder directly next to some other
  files.
  
## Overview

For a code repository, a user may ask to create and tune architectural diagrams
using **C4-PlantUML**. [C4 documentation](https://c4model.com/) C4 is a developer-friendly method
for visualizing software architecture through four hierarchical levels of
abstraction: Context, Container, Component, and Code. This approach
progressively adds detail, starting with a high-level overview of the system's
interactions and zooming into finer-grained components to communicate design
clearly. PlantUML is an open-source tool that allows users to create various
types of diagrams, including UML diagrams, from simple plain-text descriptions.

[C4-PlantUML](https://github.com/plantuml-stdlib/C4-PlantUML/blob/master/README.md) is a tool that combines the benefits of PlantUML and the C4
model. This skill covers includes, elements, relationships, and layout control
(global direction, forced edge direction, and element arrangement).

## Repo conventions

When used inside a repository:
- Store diagram sources in `$REPO_ROOT/docs/diagrams` only. Alternatively, us a
  `./diagrams/` folder directly next to some other files, if the user wants it
  that way.
- Subfolders are allowed and encouraged for scoping, e.g. `$REPO_ROOT/docs/diagrams/subsystem`.
- After creating or updating a `.puml` file, run `scripts/render.sh` on it to
  keep the output updated.
- Run `make smoke` in this skill directory after changing vendored C4 includes
  or `scripts/render.sh` to verify all C4 levels still render.
- For C4 sets, always generate an explorer HTML page with
  `scripts/build_explorer.sh`. Use flat mode for small repos and manifest mode
  for complex repos with multiple systems/containers/components.
- When a user asks for diagrams, always render a PNG by default as part of the
  response; treat rendering as implicit (do not ask separately).
- Honor user requests for SVG in addition to the default PNG.

## Default deliverables for C4 sets

When creating a C4 set, produce these artifacts by default:
- Individual rendered diagrams for:
  - System Landscape (if provided)
  - Per-system Context and Container
  - Per-container Component diagrams (only for architecturally important or
    confusing containers)
  - Deployment (if provided)
  - Sequence (if provided, one or more scenarios)
- One interactive explorer HTML page that supports:
  - Compact mode for small repos
  - Hierarchical navigation for complex repos (Landscape -> System ->
    Container -> Component)

Recommended naming conventions (manifest paths are authoritative):
- Small repos:
  - `<basename>-landscape.(svg|png)` (optional)
  - `<basename>-context.(svg|png)`
  - `<basename>-container.(svg|png)`
  - `<basename>-component.(svg|png)`
  - `<basename>-deployment.(svg|png)` (optional)
  - `<basename>-sequence.(svg|png)` (optional)
- Complex repos:
  - `<system>-context.(svg|png)`
  - `<system>-container.(svg|png)`
  - `<system>-deployment.(svg|png)` (optional)
  - `<system>-sequence-<scenario>.(svg|png)` (optional)
  - `<system>-<container>-components.(svg|png)`
- `<basename>-c4-explorer.html`

## Optional companion ER diagram

When the architecture you are modeling depends on a relational data model,
consider producing a **focused ER diagram** as a companion artifact.

Use an ER diagram when:
- the user asks about tables, schema, foreign keys, or data relationships
- the modeled workflows clearly depend on specific database tables
- sequence/component diagrams reference concrete persisted entities
- a focused data-model view would clarify the workflow or subsystem

Do not generate a full-database ER diagram by default. Prefer a
**scenario-scoped ER diagram** that only includes the tables directly involved
in the workflows being modeled.

### ER diagram scope rules

- Include only the tables that are directly relevant to the scenario.
- Include primary keys and foreign keys.
- Include only a small set of important non-key fields that help explain the
  flow.
- Prefer readability over exhaustiveness.
- If the schema is large, create one ER diagram per scenario or subsystem
  instead of one global diagram.

Examples:
- Workspace creation/admin management:
  - `workspaces`
  - `workspace_users`
  - `profiles`
  - `workspace_site`
- Survey delivery/open tracking:
  - `surveys`
  - `survey_recipients`
  - `workspace_site_contact`
  - related parent tables such as `projects` or `workspace_site`

### Repo conventions for ER diagrams

- Store ER diagram sources in `$REPO_ROOT/docs/diagrams`.
- Use names that make the scope explicit, for example:
  - `<basename>-er.puml`
  - `<basename>-er.svg`
  - `<basename>-er.png`
  - or for narrower scopes:
    - `<basename>-workspace-survey-er.puml`
    - `<basename>-project-measures-er.puml`
- Render PNG by default.
- Honor SVG requests in addition to the default PNG.

### Relationship to C4 outputs

ER diagrams are **companion artifacts**, not replacements for C4 diagrams.

- Keep the primary deliverables as C4 views first.
- Add ER diagrams only when they materially improve understanding of the
  modeled system.
- If the explorer tooling supports it cleanly, include the ER diagram as a
  companion view in the generated explorer hierarchy.
- If the explorer does not support ER views cleanly, still generate the ER
  artifact separately and mention it in the response.

### ER diagram content guidelines

- Show:
  - table names
  - PK/FK columns
  - important workflow columns (for example `created_by`, `survey_id`,
    `contact_id`, `opened_at`, `sent_at`)
- Avoid:
  - every column in the table
  - low-value audit/meta columns unless they matter to the workflow
  - unrelated catalog/reference tables unless required for understanding

### Source-of-truth guidance

When generating ER diagrams:
- Prefer actual schema definitions from migrations, DDL, or database metadata
  over product docs.
- Use product docs only as a fallback or to clarify intent.
- If code and docs disagree, prefer the schema/code and state the inference
  clearly.

## C4 Planning Step (Do This Before Drawing)

Before generating diagrams, plan the C4 scope explicitly:
- Identify whether this repo is one system or multiple systems.
- If multiple systems, create a System Landscape and list the systems to model.
- For each system, create:
  - System Context
  - Container diagram
- For each system, list containers that actually need component diagrams.
- Create component diagrams only for containers that are architecturally
  important, complex, or unclear.
- Add Deployment and Sequence diagrams only where they clarify runtime behavior
  or a critical scenario.

### Planning rule for ER diagrams

Before drawing an ER diagram:
- identify the workflow being explained
- list the minimum table set involved
- confirm the parent-child and junction-table relationships
- decide whether the ER diagram belongs as:
  - a companion to a sequence diagram
  - a companion to a component/container view
  - or a standalone data-model appendix

## Always render, always validate

When using the skill, always remember:
- Always render a diagram to PNG after any edits. Make sure diagrams are always
  up to date.
- Once rendered, review the diagram visually using your built-in image viewer
  to validate that the request of the user has been adhered to, especially
  regarding layout:
  - it should be preferably landscape
  - no overlapping text labels
  - no boundary-title collisions with nested nodes/containers
  - context/container diagrams should use the 2D space (avoid tall one-column
    "string" layouts)
- For deployment diagrams specifically:
  - check for cramped node headers before rewriting the whole layout
  - treat `Deployment_Node_L/R`, `Node_L/R`, and wrap-width tuning as the
    first-line fixes
  - if `Rel_*` labels become cluttered, prefer plain PlantUML arrows for the
    relationships while keeping C4 nodes/containers
- For companion ER diagrams:
  - verify that all shown FK relationships are supported by schema/code
    evidence
  - verify that the diagram is legible and not overcrowded
  - reduce scope if the result becomes too dense

### Layout stabilization rule (required for larger L1/L2 diagrams)

For **System Context** and **Container** diagrams with more than 4 elements
(actors/systems/containers), do not rely on generic `Rel(...)` edges alone.
Use this rule:

- Add `LAYOUT_LANDSCAPE()` before element declarations.
- Anchor at least 3 placements with directional relationships (`Rel_L`, `Rel_R`,
  `Rel_U`, `Rel_D`).
- Use `Lay_*` macros to spread peer elements and avoid single-column stacking.
- Re-render and adjust if the result is a tall/linear chain instead of a
  balanced 2D layout.

This rule is intentionally not required for very small diagrams (3-4 nodes),
where pure auto-layout is often fine.

## C4

### Core ideas

* **Goal:** communicate software architecture at *multiple zoom levels* with
  consistent, simple shapes.
* **Elements:** **Person**, **Software System**, **Container**, **Component**
  (+ **Code** level is optional).
* **Relationships:** directed connections with a short verb phrase (what talks
  to what, and why).

### Diagram levels & intent

* **System Landscape (optional):** where your system sits among other systems
  and user types.
* **Level 1 — System Context:** who uses the system and which neighboring systems it
  exchanges data with. *Scope:* one software system.
* **Level 2 — Container:** high-level building blocks (apps, services, UIs, DBs, queues)
  inside the system and their responsibilities/tech. *Scope:* deployable/runtime units.
* **Level 3 — Component:** internals of a single container: components (cohesive
  units—modules, services, controllers) and their dependencies. *Scope:* design boundaries
  within a container.
* **Level 4 — Code (optional):** selected classes/functions to illustrate a pattern.
  *Scope:* only when it clarifies.

### Element types

* **Person:** human actor (user or role). *Intent:* who benefits or initiates interactions.
* **Software System:** major system (the thing you’re building or external). *Intent:* sets
  the boundary for the other levels.
* **Container:** deployable/runtime unit (service, SPA, mobile app, DB, message broker).
  *Intent:* responsibilities + technology + interface.
* **Component:** cohesive chunk inside a container (e.g., “PaymentService,”
  “OrderController”). *Intent:* purpose + interface; hides implementation details.

### Relationships (edges)

* **Direction:** A → B (caller → callee / data flow direction).
* **Label:** short verb + purpose (e.g., “reads from,” “publishes events to”).
* **Tech/Protocol (optional):** HTTP/JSON, gRPC, JDBC, AMQP, etc.
* **Notes:** security constraints, sync/async, rate/volume if useful.

### Notation guidelines

* **Name + short description** on every box.
* **Responsibilities** over implementation detail.
* **Technology** on containers (and components if clarifying).
* **Boundaries:** group related elements (system boundary; container scope).
* **Keep it legible:** few boxes/lines per view; create more views instead of clutter.

### Supporting views (often used)

* **Dynamic diagram (scenario):** sequence of calls across elements for a use case.
* **Deployment diagram:** nodes (environments, VMs, k8s, serverless) and which containers
  run where.

## PlantUML

**PlantUML** is a text-based diagramming tool. Diagrams are written as plain
text and rendered into images (SVG, PNG, PDF). This makes diagrams easy to
version, review, and generate automatically.

### Why PlantUML
- **Version control friendly** (plain text)
- **Fast to write and modify**
- **Automatable** (CLI, CI/CD, Docker)
- **Wide diagram support**

### Supported diagram types
- Sequence diagrams
- Class diagrams
- Component and deployment diagrams
- Activity diagrams
- State diagrams
- C4-style architecture diagrams
- Mind maps and more

### Basic example

```plantuml
@startuml filename
Alice -> Bob: Hello
Bob --> Alice: Hi
@enduml
```


## Compile .puml files to SVG, PDF, PNGs 

In the `scripts/` folder of this skill, there is a convenience wrapper
`scripts/render.sh` to run it either locally or via Docker.

The wrapper preloads the vendored C4 bundle and can auto-detect common C4
sequence markers (for example `Boundary_End()` / `SHOW_INDEX()`) to switch to a
sequence-safe preload bundle. If needed, you can force sequence mode with
`--c4-sequence`.

Do not use `SHOW_INDEX()` in Context, Container, or Deployment diagrams with
this wrapper. It is treated as a sequence marker and can switch rendering into
sequence-safe preload mode, which is the wrong mode for those diagram types.

You can convert a PlantUML file to a raster or vector format as follows:

```bash
cat assets/simple.puml | scripts/render.sh -tpng -pipe > simple.png
``` 

```bash
cat assets/simple.puml | scripts/render.sh -tsvg -pipe > simple.svg
```

### C4-styled sequence diagram example (with `Boundary_End()`)

This works with `scripts/render.sh` without explicit `!include` lines:

```plantuml
@startuml payment-sequence
SHOW_INDEX()

Person(user, "User")
System(system, "Payments Core")

Container_Boundary(app_boundary, "API Container")
  Container(api, "Payments API", "Node.js", "Handles payment requests")
Boundary_End()

Rel(user, api, "Submits payment")
Rel(api, system, "Calls")
@enduml
```

Render:

```bash
cat docs/diagrams/payment-sequence.puml | scripts/render.sh -tsvg -pipe > docs/diagrams/payment-sequence.svg
```

If auto-detection misses a sequence-specific file, force the sequence-safe
preload bundle explicitly:

```bash
cat docs/diagrams/payment-sequence.puml | scripts/render.sh --c4-sequence -tsvg -pipe > docs/diagrams/payment-sequence.svg
```

## Interactive Explorer Output

Use `scripts/build_explorer.sh` to produce a one-page C4 explorer.

### Mode 1: Small Repo Mode (flat flags, default/simple)

Use this when the repo maps cleanly to one main system and one primary
component diagram.

```bash
scripts/build_explorer.sh \
  --out docs/diagrams/payments-c4-explorer.html \
  --title "Payments Architecture C4" \
  --readme README.md \
  --landscape docs/diagrams/payments-landscape.svg \
  --context docs/diagrams/payments-context.svg \
  --container docs/diagrams/payments-container.svg \
  --component docs/diagrams/payments-component.svg \
  --deployment docs/diagrams/payments-deployment.svg \
  --sequence docs/diagrams/payments-sequence.svg
```

Flat mode behavior:
- `--context`, `--container`, and `--component` are required.
- `--landscape`, `--deployment`, and `--sequence` are optional.
- `README.md` is read by default and the first meaningful paragraph is shown at
  the top as a short system description. Override with `--readme <path>`.
- The explorer auto-renders in compact mode for small repos.

### Mode 2: Complex Repo Mode (hierarchical manifest)

Use a JSON manifest when the repo contains multiple systems, multiple
containers, and multiple component diagrams.

```bash
scripts/build_explorer.sh \
  --manifest docs/diagrams/c4-explorer.json \
  --out docs/diagrams/platform-c4-explorer.html
```

You can override manifest metadata at runtime:

```bash
scripts/build_explorer.sh \
  --manifest docs/diagrams/c4-explorer.json \
  --out docs/diagrams/platform-c4-explorer.html \
  --title "Platform C4 Explorer" \
  --readme README.md
```

Manifest mode behavior:
- `--manifest` and the flat diagram flags are mutually exclusive.
- Hierarchy comes from the manifest (Landscape -> Systems -> Containers ->
  Components).
- `readme` and diagram paths in the manifest are resolved relative to the
  manifest file location by default. CLI overrides like `--readme` are resolved
  relative to the current working directory.
- Context and Container diagrams are required per system.
- Component diagrams are required for each declared component entry.
- Missing optional Landscape/Deployment/Sequence diagrams are skipped with a
  warning.
- The explorer auto-renders tree navigation for complex repos and compact mode
  for smaller manifests.

### Manifest Schema (JSON)

```json
{
  "title": "Payments Platform",
  "readme": "README.md",
  "landscape": "docs/diagrams/payments-landscape.svg",
  "systems": [
    {
      "id": "payments_core",
      "name": "Payments Core",
      "context": "docs/diagrams/payments-core-context.svg",
      "container": "docs/diagrams/payments-core-container.svg",
      "deployment": "docs/diagrams/payments-core-deployment.svg",
      "sequences": [
        {
          "id": "auth_flow",
          "name": "Payment Authorization Flow",
          "diagram": "docs/diagrams/payments-core-sequence-auth.svg"
        }
      ],
      "containers": [
        {
          "id": "api",
          "name": "Payments API",
          "components": [
            {
              "id": "api_components",
              "name": "API Components",
              "diagram": "docs/diagrams/payments-api-components.svg"
            }
          ]
        }
      ]
    }
  ]
}
```

Manifest checklist:
- Use stable IDs (`id`) for systems, containers, sequences, and components.
- Keep `readme` and diagram paths relative to the manifest file when possible.
- Declare only the component diagrams that add architectural value.
- Add multiple sequence diagrams only for important scenarios.

### Explorer UI behavior

- Header includes:
  - C4 intro paragraph and link to the C4 model
  - README-derived system summary paragraph
- Footer includes provenance metadata for traceability:
  - UTC creation timestamp
  - short git commit hash
  - optional `(dirty)` marker when generated from a repo with uncommitted changes
- Clicking a diagram opens it in a fullscreen viewer; close with Escape,
  backdrop click, or the close button.
- Prefer SVG inputs when available; use PNG otherwise.

### Smoke test workflow (C4 preload validation)

Use the built-in smoke test to catch regressions in the vendored C4 preload
bundle or render wrapper:

```bash
cd public/c4-diagrams
make smoke
```

This renders minimal diagrams for:
- context
- container
- component
- dynamic
- sequence
- deployment

The command fails if any render fails.

Optional layout heuristic helper:
- `scripts/check_layout_aspect.sh <svg>` warns when a diagram is suspiciously
  tall (e.g., top-down "string" layout instead of balanced 2D usage).
- The same check also warns when a boundary title block is too close to nested
  content, which is a common deployment-node legibility failure.
- `scripts/smoke_test.sh` runs this check automatically for
  context/container/deployment smoke outputs.
- Set `C4_LAYOUT_FAIL_ON_WARN=1` to make suspicious aspect ratios fail CI/smoke
  runs.


## C4-PlantUML

**C4-PlantUML** is a PlantUML-based library that lets you describe software
architecture using the C4 model (Context, Container, Component, Code) as
concise, text-based diagrams that are easy to version and automate.


### Basic Syntax

This is the syntax for a file `my-systems.puml`.

```plantuml
@startuml my-systems
' C4 macros are auto-loaded by scripts/render.sh

Person(user, "User", "A user of the system")
System(system, "My System", "Does something useful")

Rel(user, system, "Uses")
@enduml
```

### Include the library

Prefer the vendored local bundle. `scripts/render.sh` preloads
`assets/includes/C4/C4_All.puml` and sets `RELATIVE_INCLUDE` to the local bundle path,
so the full C4 macro stack is available without adding any include line in your
diagram:
- `C4_Context` (system landscape/context macros such as `Person`, `System`,
  `System_Ext`, `Rel`, `Rel_*`, `Lay_*`)
- `C4_Container`
- `C4_Component`
- `C4_Dynamic`
- `C4_Deployment`
- `C4_Sequence`

``` plantuml
' Preferred with this skill: no explicit include required.
' scripts/render.sh preloads the local vendored C4 bundle.

' Optional fallback if you do not use scripts/render.sh:
' !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
```

## Troubleshooting C4 include/preload failures

Common symptoms:
- `Unknown keyword` / `Undefined procedure` for macros like `Person`, `System`,
  `Rel`, `Container`, `Component`, `RelIndex`, or `Deployment_Node`
- Diagram renders only when you add explicit `!include` lines

Checks:
- Confirm you are rendering via `scripts/render.sh` (not raw `plantuml`)
- Confirm `assets/includes/C4/C4_All.puml` includes the full C4 set (Context,
  Container, Component, Dynamic, Deployment, Sequence)
- For C4-styled sequence diagrams that use `Boundary_End()`, confirm the
  sequence-safe bundle `assets/includes/C4/C4_All_Sequence_Last.puml` exists
  and that `scripts/render.sh` auto-detected sequence markers (or use
  `--c4-sequence`)
- Confirm `RELATIVE_INCLUDE` points at the vendored `assets/includes/C4`
  directory (the wrapper injects this automatically)
- Run `make smoke` in `public/c4-diagrams` to validate preloading across all C4
  levels
- Run `scripts/check_layout_aspect.sh <diagram.svg>` if a context/container
  diagram looks suspiciously tall or linear, or if a deployment boundary title
  looks crowded against nested nodes/containers

If `scripts/render.sh` fails, it may print a hint about checking
preload bundle contents and the injected `RELATIVE_INCLUDE` path.

### Layout orientation

To control the layout orientation, place these **before** element declarations:

``` plantuml
' Preferred: keeps Rel_* meaning stable (Up is up, Right is right)
LAYOUT_LANDSCAPE()

left to right direction     ' or: top to bottom direction

' Legacy, do not use: rotates directions (Up becomes Left, etc.)
' LAYOUT_LEFT_RIGHT()
```

### Control relationship direction (edge routing)

Use directional relationship macros to force relative placement:

``` plantuml
Rel_U(a, b, "Up")      ' b above a
Rel_D(a, c, "Down")    ' c below a
Rel_L(a, d, "Left")    ' d left of a
Rel_R(a, e, "Right")   ' e right of a
BiRel_R(x, y, "Bidirectional to the right")
```

For deployment diagrams, explicitly prefer `Deployment_Node_L/R` and `Node_L/R`
when sibling nodes need clearer left/right anchoring. Generic `Deployment_Node`
plus `Rel(...)` is often not enough once nested runtime boundaries start to
crowd each other.

### Deployment legibility

When a deployment diagram starts looking cramped, apply these fixes in this
order before redrawing the whole view:

- Use `Deployment_Node_L/R` and `Node_L/R` to spread sibling runtime nodes
  horizontally.
- Tune the C4 deployment wrapping variables first:
  - `$NODE_TYPE_MAX_CHAR_WIDTH`: wraps the node type line
  - `$NODE_DESCR_MAX_CHAR_WIDTH`: wraps the node description block
  - `$DEFAULT_WRAP_WIDTH`: global fallback wrapping width
- Re-check boundary title spacing after each change. A readable overall aspect
  ratio is not enough if long node headers collide with nested content.
- If C4 `Rel_*` labels become cluttered, switch those relationships to plain
  PlantUML arrows and keep the C4 nodes/containers. This is the preferred
  fallback for dense deployment views.

Example:

```plantuml
@startuml
' C4 macros are auto-loaded by scripts/render.sh
LAYOUT_LANDSCAPE()

!$NODE_TYPE_MAX_CHAR_WIDTH = 18
!$NODE_DESCR_MAX_CHAR_WIDTH = 22
!$DEFAULT_WRAP_WIDTH = 24

Deployment_Node(cluster, "Kubernetes Cluster", "k3s", "Primary runtime boundary") {
  Deployment_Node_L(api_node, "API Node", "VM", "Ingress and API workloads") {
    Container(api, "API", "FastAPI", "Handles requests")
  }
  Deployment_Node_R(worker_node, "Worker Node", "VM", "Async worker workloads") {
    Container(worker, "Worker", "Python", "Processes jobs")
  }
}

api --> worker : Publishes jobs
@enduml
```

### Arrange elements without relationships

When two elements have no edge but their relative position matters, use `Lay_*`
macros:

``` plantuml
Lay_U(a, b)
Lay_R(a, c)
Lay_D(a, d)
Lay_L(a, e)

' Increase spacing (esp. with floating legends):
Lay_Distance(a, LEGEND(), 2)
```

### Practical layout tips

* Declare nodes in a logical order; PlantUML uses declaration order as a hint. If
  auto-layout fights you, switch to `Rel_*`/`Lay_*`.
* Inside boundaries, you can still use `Rel_*` and `Lay_*` to stabilize placement.
* Be careful about auto vs manual layout and element ordering: Even though you
  can force layouts with `Lay_*()`, you should remember that PlantUML (and
  C4-PlantUML) are fundamentally automatic layout systems. For large diagrams,
  heavy manual placement may backfire. The order in which you declare elements
*and* their relationships influences layout. [crashedmind.github.io](https://crashedmind.github.io/PlantUMLHitchhikersGuide/layout/layout.html)
* If edges still cross awkwardly, nudge with `Lay_*` or reorder declarations.
* Make sure you don't have overlapping text labels
* If layout goes weird, try:
  * Re-ordering your element declarations
  * Using hidden/"invisible" relationships to enforce grouping or ranking
  * Minimizing forced placements only where really needed

### Balanced 2D template (default pattern for Context/Container diagrams)

When a Context or Container diagram starts rendering as a vertical chain, use a
"hub-and-spoke" layout pattern around the primary system/container instead of a
linear `Rel(...)` chain.

Template (adapt names/macros to Context vs Container level):

```plantuml
@startuml
' C4 macros are auto-loaded by scripts/render.sh
LAYOUT_LANDSCAPE()
left to right direction

' Place the primary system/container near the center
Person(user, "Primary User", "Main actor")
Person(admin, "Admin", "Operations/maintenance")
System(system, "My System", "System in scope")
System_Ext(ext_api, "External API", "Upstream/downstream integration")
System_Ext(ext_queue, "Message Broker", "Async messaging")
System_Ext(ext_store, "Data Store", "Persistence / files")

' Anchor positions (use directional relations for placement + semantics)
Rel_R(user, system, "Uses")
Rel_D(admin, system, "Administers")
Rel_R(system, ext_api, "Calls")
Rel_D(system, ext_queue, "Publishes/Consumes")
Rel_L(ext_store, system, "Stores state for")

' Optional peer spacing hints to use 2D space better
Lay_U(user, admin)
Lay_R(ext_api, ext_queue)
Lay_D(ext_queue, ext_store)
@enduml
```

Practical guidance:
- Put the system-in-scope near the center of the diagram.
- Group humans/actors on one side and external systems on the opposite side.
- Use `Rel_*` for the primary placement hints, then `Lay_*` only to nudge.
- If a diagram is still too tall, reduce crossings by reordering declarations
  before adding more layout constraints.

### Use sprites/icons and hyperlinks to enrich elements

You don’t have to stick to plain boxes. C4-PlantUML supports `$sprite` and
`$link` (and even `$descr`) properties for elements, making your diagrams
richer and more interactive.  For example:

This enhances readability (icon for the type) and traceability (link to docs).

**Tip:** Use icons sparingly — too many visual flourishes can distract from the
architecture story.

### Tag elements and relationships for legends / filtering

You can add tags to elements and relationships (via macros like `AddElementTag`,
`AddRelTag`) so you can group or highlight things (versions, deprecated, backup, etc).
Then you can use `SHOW_LEGEND()` or `LAYOUT_WITH_LEGEND()` and the legend will show the
meaning of those tags.

**Tip:** Use this when you need to communicate extra metadata (e.g., “v1.0”, “deprecated”,
“beta”) without cluttering the main diagram.

## Open Questions

- Ask whether to create diagrams in `$REPO_ROOT/docs/diagrams` (default) of the
  user wants to have a `./diagrams/` directly folder next to some other files.