Document from code

There are two ways to use CRAYON-C4. You can design a new system from scratch in the visual editor — open the C4 editor and draw it node by node. Or, if the system already exists in code, you can document it as it evolves: generate a model from the repository, refine it by hand, and re-generate whenever the code moves. This page covers the second path.

The promise that makes this practical: re-running the generator never silently discards your manual curation. Renames, descriptions, grouping, and layout you added by hand survive every update — a three-way merge folds in what changed in the code and keeps what you changed by hand.

The loop

Step 1 — Generate

Run the generator on your repository. It scans the codebase and produces a portable model file validated against the published CRAYON-C4 schema.

Step 2 — Curate

Open the model in a CRAYON-C4 project and refine it by hand. The generator can infer structure and relationships from code patterns, but it can't write good names, descriptions, grouping decisions, and layout. You add that context.

Step 3 — Re-run and merge

When the code changes, generate again. A three-way merge compares what changed in the code against what you changed by hand. You review every add, change, and removal before it lands — and your manual edits survive intact.

You can drive this from three surfaces: the command line, VS Code, and inside the web app.

Generate from the command line

The generator is a zero-config CLI that understands Next.js, React, .NET, and Java (Maven & Gradle) codebases. From the root of any repository:

npx crayon-c4 .

It scans the project and writes three files:

FilePurpose
crayon-c4-project.jsonThe model file — the C4 diagram data, validated against the schema. This is what you import into a project.
crayon-c4-provenance.jsonA sidecar that records what was generated and when. It's what makes re-runs safe: the merge engine uses it to distinguish your hand-authored elements from generated ones. Keep this alongside the model file.
crayon-c4-enrichment.jsonA task list of the elements whose descriptions the generator couldn't fill from code. The AI enrichment pass (CLI or VS Code) works through this list; you review its edits before they land.

CLI reference

All flags are optional. A zero-flag run (npx crayon-c4) produces a usable model.

FlagPurposeDefault
pathRepository root to scan (positional). (current directory)
--root <path>Scope the scan to a sub-project; repeat the flag for several roots in a monorepowhole repository
--depth <level>How deep the model goes: context, container, component, or codecomponent
--out <file>Where to write the model file<path>/crayon-c4-project.json
--provenance <file>Where to write the provenance sidecar<path>/crayon-c4-provenance.json
--starting <file>A previous model file to merge into (for re-runs)none
--exclude <globs>Extra exclusion patterns (comma-separated)engine defaults
--config <file>Custom extractor config fileauto-discovered
--file-cap <n>Maximum files to analyzeengine default
--applyWrite merged result (vs report-only)off
--dry-runReport only, write nothingoff
--jsonMachine-readable JSON summary on stdouthuman-readable text

Exit codes: 0 = success, 1 = unexpected error, 2 = usage/config error, 3 = no stack detected, 4 = invalid starting model, 5 = output fails validation.

Project configuration: .crayon/config.json

Settings that should travel with the analyzed repository live in a .crayon/config.json file at its root (an explicit --config path wins; the legacy root-level crayon-extract.config.json is still honoured). The file must carry "schema": 1 and supports:

  • prompts — project-scoped instructions for the AI passes: append applies to every pass, enrichment and modeling to their specific pass. These merge before any editor-level prompt setting, so the repo's own conventions always come first.
  • projects — shape the model: exclude drops matched systems/containers (anchored, case-insensitive globs) with all their descendants and touching connections, and bundles merges matched containers into one named container. The run report lists everything excluded or bundled — nothing is hidden silently, and surviving element ids never change.
  • flows — entryPoints marks components (API routes, UI components, controllers) that get a fully traced, method-level F4 flow diagram, with an optional per-entry maxDepth.
  • static — scan tuning: roots (the config-file equivalent of --root), extra exclude patterns, and analysis bounds.

The CLI and the VS Code extension share one engine, so the config means the same thing in both.

Generate from VS Code

If you'd rather not leave your editor, the CRAYON-C4 VS Code extension contributes these commands (run from the command palette):

  • CRAYON-C4: Generate Model — scan the project and create a model file
  • CRAYON-C4: Regenerate — re-run the generator on an existing model file
  • CRAYON-C4: Open Architecture Viewer — open the interactive viewer directly on the generated model
  • CRAYON-C4: Open Web App — open the web app's import page in your browser, where you pick the generated model file to merge (nothing is uploaded — the path is put on your clipboard)
  • CRAYON-C4: Enrich Descriptions with AI — fill empty descriptions in the model using AI
  • CRAYON-C4: Extract Decisions with AI — recover the architecture decisions behind the code into the .adr slice, linked to the elements they concern
  • CRAYON-C4: Generate Model with AI — build the whole model (C4 + deployment) from an AI reading the repo, when the static scanners do not cover your stack

These commands use the same engine as the CLI — it's all one generator, wired into the editor for convenience. The VS Code extension page covers the full workflow: installation, the built-in viewer, the AI passes, settings, and multi-project workspaces.

Create a new project from generated code

Once you have a generated model file, bring it into a CRAYON-C4 project:

  1. Go to Projects.
  2. Click New project and choose From code — a file picker opens for the generated crayon-c4-project.json.
  3. Optionally include the crayon-c4-provenance.json sidecar in the same pick (recommended) so future merges know which parts were generated.
  4. A new project is created with the generated model.

From code is available on every tier, including anonymous — as is Update from code below. The whole path is complimentary: the CLI and the VS Code extension run on your own machine, and a generated model of any size can be brought in, opened, drilled through and exported whatever plan you are on. Your plan's element limit governs how much of it you can edit, never how much you can hold or read.

Keep it in sync: Update from code

When the code changes, generate again, then sync the project:

  1. Go to Projects and open the project's detail page (works for local and cloud projects alike).
  2. Click Update from code, pick the newly generated model file, and optionally include the provenance sidecar.
  3. A review opens showing every add, change, and removal. Conflicts (where the code renamed something and you renamed it too) are flagged for you to pick.
  4. Apply the review to merge the changes into your project.

Your hand edits are preserved: code-derived updates land, your curated names, grouping, and layout stay, and removals are opt-in so hand-authored elements are never silently dropped.

Text is offered, not overwritten. Where the file carries a description, short description, or connection label different from yours, the review lists it under Text from the file with Keep mine selected — and Take all from the file flips the lot in one click. That is the route for text you added to the model file yourself, by hand or from an AI enrichment pass. Where you had written nothing, the file's text is used outright; where the file says nothing, yours is never blanked out.

How matching works across runs

The merge only works because every generated element carries an id that's derived from where it lives in your code — its namespace, package, module path, or project name — rather than a random one or its position in the scan. Generate the same repository twice with nothing changed, and every element comes back with the exact same id both times. That's what lets run 2 recognize "this is the Orders component from run 1" instead of treating it as a stranger.

The provenance sidecar (crayon-c4-provenance.json) is the record of that: for every generated element, it remembers the code-derived id, the name the code gave it, and when it was first and last seen. Bring it in alongside the model file — either at import or at each Update from code — and the merge can tell:

  • An id present in both the old and new file is the same element continuing to exist. Anything the code determines (its type, its technology, what contains it, its code relationships) is refreshed from the new file; anything you curated (its name, if you renamed it, its description, its grouping, its position on a diagram) is kept.
  • An id only in the new file is a genuine addition — something new in the code since last time.
  • An id only in the old file is only ever treated as a removal candidate, and only when it's clearly code-generated (it's in the provenance sidecar). Anything you added by hand — an element with no provenance entry — is left alone; there's nothing in the new scan to compare it against, so it's assumed to still matter and is never swept away silently.
  • The code renamed something and you also renamed it — the one case the merge can't resolve on its own — is flagged as a conflict for you to pick between.

Two things worth knowing that follow directly from this:

  • A meaningful move usually reads as delete + add, not a rename. Moving a class to a different namespace, renaming a project, or restructuring folders changes the very thing the id is derived from, so the next run sees it as a new element rather than the old one relocated. The removal is still opt-in and the addition still lands cleanly — you just won't get automatic rename-tracking across a structural move the way you would for a plain code-level rename.
  • Without the provenance sidecar, matching still happens, but more cautiously. The merge falls back to comparing values field by field: your curated fields are still protected, but nothing is auto-detected as a rename or flagged as a conflict, and everything only in your project is treated as hand-authored (never removed) rather than as something the extractor once produced. Keeping the sidecar alongside the model file is what makes the sharper behavior above — real rename tracking, real conflict flags — available.

Hand-author a model instead of generating

If you prefer to write your own model file instead of generating from code, you can. The published CRAYON-C4 JSON Schema plus an authoring guide let you paste a system description into any AI (or write by hand) to produce a valid model. Then import it as a new project, the same way you would a generated file.

Where next