Turning text into diagrams with AI: field notes from documenting a side project
From Static Canvas to Living Text: Engineering a Self-Healing Documentation Pipeline
Current Situation Analysis
Architecture documentation suffers from a predictable decay curve. When engineering teams embed static images in repositories, those visuals typically remain accurate for roughly two development cycles. The moment a microservice boundary shifts, a message queue gets partitioned, or an API gateway is introduced, the diagram becomes misleading. Teams face a binary choice: manually redraw the asset in a GUI editor, or let the documentation rot. Both paths degrade system reliability. Manual updates introduce latency between implementation and documentation, while stale visuals increase cognitive load during incident response, capacity planning, and onboarding.
The core misunderstanding lies in treating diagrams as finished artifacts rather than compiled outputs. Traditional drawing tools decouple the visual representation from the underlying system state. When the source of truth lives in a proprietary canvas file or a PNG export, regeneration requires human intervention. Text-driven diagram generation flips this model. By storing architecture as structured text or lightweight markup, teams decouple layout from logic. Regenerating a visual becomes a deterministic operation that takes seconds rather than hours. This approach aligns documentation with version control, enables peer review through pull requests, and ensures that the diagram always reflects the current commit state.
Industry observations consistently show that documentation half-life correlates directly with sprint velocity. Manual diagram maintenance consumes approximately 15-20% of total documentation time, yet yields zero runtime value. When diagrams are treated as code, the maintenance overhead shifts from manual redrawing to schema validation and prompt engineering. The economic shift is substantial: regeneration cost drops from hours to seconds, review friction decreases through text diffs, and architectural drift becomes visible immediately in CI pipelines. The problem is rarely tooling; it is workflow design. Teams that treat diagrams as static assets will always fight decay. Teams that treat them as compiled outputs will achieve self-healing documentation.
WOW Moment: Key Findings
Evaluating multiple generation strategies reveals a clear trade-off surface between precision, maintainability, and audience readiness. The following comparison isolates the operational characteristics of each approach based on real-world implementation patterns:
| Approach | Maintenance Overhead | Structural Precision | PR Reviewability | Stakeholder Readiness | Regeneration Latency |
|---|---|---|---|---|---|
| Canvas Export (PNG/SVG) | High (manual redraw) | High (pixel-perfect) | None (binary diff) | High (polished) | Hours |
| Mermaid + LLM Synthesis | Low (text regeneration) | High (syntax-validated) | Full (line-by-line diff) | Medium (requires styling) | Seconds |
| Prose-to-Visual (Napkin-style) | Medium (prompt iteration) | Low (conceptual only) | Limited (image output) | High (narrative flow) | Minutes |
| AI Deck Synthesis | Medium-High (heavy editing) | Low (template-driven) | None | High (presentation-ready) | Minutes-Hours |
The data shows that Mermaid combined with LLM-assisted syntax generation occupies the optimal quadrant for engineering workflows. It preserves structural accuracy while enabling version-controlled reviews. Prose-to-visual tools excel at abstract communication but lack the deterministic output required for infrastructure mapping. AI presentation generators reduce slide creation time but demand substantial manual refinement before technical accuracy can be guaranteed.
The critical insight is that no single tool solves the entire documentation lifecycle. Instead, routing output to the appropriate channel based on audience and precision requirements yields the highest ROI. Engineering teams should maintain a canonical text schema for infrastructure, use prose-to-visual generators for design reviews, and reserve AI deck synthesis for executive syncs. The moment AI output is treated as a final deliverable rather than a first draft, quality degrades and technical debt accumulates. Human validation remains the non-negotiable layer between generation and publication.
Core Solution
Building a self-healing documentation pipeline requires three layers: a canonical text schema, an LLM translation layer, and a rendering/review surface. The implementation prioritizes determinism, version control integration, and human validation. Below is a production-grade architecture that scales across multi-service environments.
Step 1: Define a Canonical Architecture Schema
Instead of freeform prose, structure system topology as a lightweight JSON configuration. This schema captures nodes, edges, and metadata without enforcing visual layout. Structured data enables validation, prevents syntax drift, and allows programmatic querying.
// src/types/architecture.ts
export interface ServiceNode {
id: string;
classification: 'compute' | 'storage' | 'gateway' | 'queue' | 'external';
inboundDeps: string[];
outboundDeps: string[];
annotations?: Record<string, string>;
}
export interface TopologyManifest {
schemaVersion: string;
environment: 'dev' | 'staging' | 'prod';
services: ServiceNode[];
crossBoundaryLinks?: Array<{
source: string;
target: string;
transport: string;
encryption: boolean;
}>;
}
Step 2: Implement the Deterministic Compiler
The LLM acts as a syntax compiler, converting the manifest into Mermaid syntax. Constrained prompting prevents hallucination and ensures valid graph definitions. A deterministic compiler layer sits between the LLM and the output to guarantee structural integrity.
// src/compilers/mermaid-builder.ts
import { TopologyManifest, ServiceNode } from '../types/architecture';
function formatNodeLabel(node: ServiceNode): string {
const base = `${node.id}["${node.id} (${node.classification})"]`;
const annotations = node.annotations
? Object.entries(node.annotations).map(([k, v]) => `${k}: ${v}`).join(' | ')
: '';
return annotations ? `${base}:::${annotations}` : base;
}
function formatEdge(source: string, target: string, transport?: string, encrypted = false): string {
const style = encrypted ? '-.->|🔒' : '-->';
const label = transport ? `|${transport}|` : '';
return ` ${source} ${style}${label} ${target}`;
}
export function compileTopologyToMermaid(manifest: TopologyManifest): string {
const nodeDefinitions = manifest.services.map(formatNodeLabel).join('\n ');
const edgeDefinitions = manifest.services.flatMap(node =>
node.outboundDeps.map(dep => formatEdge(node.id, dep))
).join('\n');
const crossLinks = manifest.crossBoundaryLinks?.map(link =>
formatEdge(link.source, link.target, link.transport, link.encryption)
).join('\n') || '';
return `graph TD
classDef compute fill:#e1f5fe,stroke:#01579b
classDef storage fill:#f3e5f5,stroke:#4a148c
classDef gateway fill:#fff3e0,stroke:#e65100
classDef queue fill:#e8f5e9,stroke:#1b5e20
classDef external fill:#eceff1,stroke:#37474f,stroke-dasharray: 5 5
${nodeDefinitions}
${edgeDefinitions}
${crossLinks}
class ${manifest.services.filter(s => s.classification === 'compute').map(s => s.id).join(',')} compute;
class ${manifest.services.filter(s => s.classification === 'storage').map(s => s.id).join(',')} storage;
class ${manifest.services.filter(s => s.classification === 'gateway').map(s => s.id).join(',')} gateway;
class ${manifest.services.filter(s => s.classification === 'queue').map(s => s.id).join(',')} queue;
class ${manifest.services.filter(s => s.classification === 'external').map(s => s.id).join(',')} external;`;
}
Step 3: Integrate with Version Control and CI
Store the compiled output in a dedicated docs/architecture/ directory. Configure a pre-commit hook or CI step to regenerate the diagram when the manifest changes. This ensures the visual always matches the schema.
# .github/workflows/docs-sync.yml
name: Sync Architecture Diagrams
on:
push:
paths: ['manifests/**']
pull_request:
paths: ['manifests/**']
jobs:
validate-and-render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'npm' }
- run: npm ci
- name: Validate manifest schema
run: npx ts-node scripts/validate-manifest.ts
- name: Generate Mermaid output
run: npx ts-node scripts/render-diagrams.ts
- name: Verify syntax
run: npx mmdc -i docs/architecture/system-overview.md -o /dev/null
- name: Commit updated diagrams
if: github.event_name == 'push'
run: |
git config user.name "ci-docs-bot"
git config user.email "ci@internal.local"
git add docs/architecture/
git diff --staged --quiet || git commit -m "chore(docs): regenerate architecture diagrams"
git push origin HEAD:${{ github.ref_name }}
Architecture Rationale
JSON over raw text: Structured data enables validation, prevents syntax drift, and allows programmatic querying. LLMs excel at translation but struggle with unstructured constraints. By enforcing a strict schema, we eliminate ambiguous node definitions and ensure consistent edge routing.
Mermaid over Canvas: Native platform rendering eliminates asset hosting overhead. Text-based diffs make peer review feasible. Engineers can review architectural changes line-by-line, catching logical errors before they merge.
Human-in-the-loop: AI generation produces the first draft. Engineering review catches logical gaps, missing dependencies, and incorrect protocol annotations. Unvalidated output introduces false confidence. The pipeline treats AI as a syntax accelerator, not an architecture authority.
CI Validation: Syntax checking via mmdc prevents broken diagrams from reaching main. Schema validation catches missing dependencies or circular references before rendering. This two-layer validation ensures that only structurally sound diagrams enter the repository.
Pitfall Guide
1. Blind Layout Trust
Mermaid’s automatic routing algorithm struggles with graphs exceeding ten nodes. Edges cross, labels overlap, and hierarchy breaks. Teams often assume the renderer will handle complexity, leading to unreadable outputs.
Fix: Use subgraph clustering to enforce logical boundaries. Apply rankdir and explicit rank constraints to control vertical/horizontal flow. Manually adjust edge routing only when CI validation fails. Keep node counts under 15 per diagram; split by domain when necessary.
2. Unconstrained LLM Prompts
Freeform prompts cause syntax hallucination, missing brackets, or invalid graph directives. The output may render locally but break in CI. Model updates exacerbate this drift.
Fix: Implement a strict system prompt that enforces Mermaid syntax rules. Add a post-generation validation step using a parser like mermaid-cli to catch errors before commit. Pin prompt templates to version-controlled files and implement snapshot testing for output stability.
3. Conceptual Tools as Infrastructure Source
Napkin-style generators excel at abstract flows but lack deterministic node mapping. Using them for precise architecture creates version drift and makes PR reviews impossible. Fix: Reserve prose-to-visual tools for RFCs, design reviews, and stakeholder briefings. Maintain a separate canonical text schema for production infrastructure. Never commit AI-generated images to infrastructure repositories.
4. Skipping PR Diff Reviews
Treating auto-generated diagrams as “set and forget” removes the validation layer. Logical errors propagate silently, and architectural drift goes unnoticed until incidents occur. Fix: Enforce diagram review in pull request templates. Require explicit approval for edge additions, node removals, and protocol changes. Add a checklist item: “Verify diagram matches implementation changes.”
5. Ignoring Cross-Environment Variance
A single diagram rarely represents dev, staging, and production accurately. Overloading one graph with environment-specific nodes creates clutter and reduces readability. Fix: Parameterize the manifest with environment flags. Generate environment-specific diagrams via CI matrix builds. Keep the canonical schema environment-agnostic. Use conditional rendering in the compiler to filter nodes by target environment.
6. Prompt Version Drift
LLM model updates change output formatting. A prompt that worked last month may produce broken syntax after a provider update. Teams rarely version their prompts, leading to silent CI failures.
Fix: Store prompts in prompts/ directory with semantic versioning. Implement integration tests that compare generated output against known-good snapshots. Monitor CI failures for syntax regressions and roll back prompt versions when necessary.
7. Over-Annotation
Adding excessive metadata, color codes, and custom styling bloats the text source and reduces readability. Diagrams become harder to diff and maintain. Fix: Stick to semantic node types and clear edge labels. Apply styling through theme files rather than inline directives. Keep the source under 150 lines for optimal diff performance. Use annotations sparingly and only for critical operational metadata (e.g., SLA targets, data residency).
Production Bundle
Action Checklist
- Define a strict JSON schema for service topology and dependencies
- Implement a deterministic compiler that converts schema to Mermaid syntax
- Add LLM-assisted generation only for natural language to schema translation
- Configure CI to validate diagram syntax before merging
- Route conceptual visuals to separate RFC channels, not infrastructure repos
- Enforce peer review for all diagram changes in pull requests
- Parameterize manifests for environment-specific diagram generation
- Pin prompt versions and implement snapshot testing for output stability
- Split diagrams by domain when node count exceeds 15
- Add architecture diagram validation to onboarding documentation
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|---|---|---|
| Production infrastructure mapping | Mermaid + Schema Compiler | Deterministic, diffable, native platform support | Low (CI compute only) |
| Design RFC / Conceptual flow | Prose-to-Visual (Napkin-style) | Fast iteration, narrative clarity, stakeholder-friendly | Medium (tool subscription) |
| Executive presentation / Team sync | AI Deck Synthesis + Manual Trim | Rapid slide generation, requires heavy editing for accuracy | Medium-High (licensing + engineering time) |
| Legacy system documentation | LLM-assisted schema extraction | Converts unstructured notes into version-controlled text | Low-Medium (prompt engineering + validation) |
| Multi-region deployment topology | Parameterized Mermaid + CI Matrix | Environment-specific views without source duplication | Low (CI pipeline overhead) |
| Incident post-mortem visualization | Manual Mermaid + LLM draft | Precision required for root cause analysis, AI accelerates first pass | Low (engineering time) |
Configuration Template
Copy this structure into your repository to establish a baseline text-driven diagram pipeline.
// manifests/system-topology.json
{
"schemaVersion": "2.1.0",
"environment": "prod",
"services": [
{
"id": "api-gateway",
"classification": "gateway",
"inboundDeps": [],
"outboundDeps": ["auth-service", "order-processor"],
"annotations": { "sla": "99.95%", "region": "us-east-1" }
},
{
"id": "auth-service",
"classification": "compute",
"inboundDeps": ["api-gateway"],
"outboundDeps": ["user-db"],
"annotations": { "runtime": "node:20", "scaling": "horizontal" }
},
{
"id": "order-processor",
"classification": "compute",
"inboundDeps": ["api-gateway"],
"outboundDeps": ["event-bus", "inventory-db"],
"annotations": { "runtime": "go:1.21", "scaling": "vertical" }
},
{
"id": "event-bus",
"classification": "queue",
"inboundDeps": ["order-processor"],
"outboundDeps": ["notification-worker"],
"annotations": { "type": "kafka", "retention": "7d" }
},
{
"id": "notification-worker",
"classification": "compute",
"inboundDeps": ["event-bus"],
"outboundDeps": ["email-provider"],
"annotations": { "runtime": "python:3.11", "scaling": "event-driven" }
}
],
"crossBoundaryLinks": [
{ "source": "order-processor", "target": "inventory-db", "transport": "gRPC", "encryption": true }
]
}
<!-- docs/architecture/system-overview.md -->
```mermaid
graph TD
classDef compute fill:#e1f5fe,stroke:#01579b
classDef storage fill:#f3e5f5,stroke:#4a148c
classDef gateway fill:#fff3e0,stroke:#e65100
classDef queue fill:#e8f5e9,stroke:#1b5e20
classDef external fill:#eceff1,stroke:#37474f,stroke-dasharray: 5 5
api-gateway["api-gateway (gateway)"]
auth-service["auth-service (compute)"]
order-processor["order-processor (compute)"]
event-bus["event-bus (queue)"]
notification-worker["notification-worker (compute)"]
user-db["user-db (storage)"]
inventory-db["inventory-db (storage)"]
email-provider["email-provider (external)"]
api-gateway --> auth-service
api-gateway --> order-processor
auth-service --> user-db
order-processor --> event-bus
order-processor --> inventory-db
event-bus --> notification-worker
notification-worker --> email-provider
order-processor -.->|🔒gRPC| inventory-db
class api-gateway gateway;
class auth-service,order-processor,notification-worker compute;
class user-db,inventory-db storage;
class event-bus queue;
class email-provider external;
### Quick Start Guide
1. Create a `manifests/` directory and add a JSON topology file following the schema structure. Validate it against the TypeScript interface using `npx ts-node scripts/validate-manifest.ts`.
2. Install the rendering dependencies: `npm install -D @mermaid-js/mermaid-cli typescript ts-node`.
3. Run the compiler script to generate the Markdown file: `npx ts-node scripts/render-diagrams.ts`. Verify the output in `docs/architecture/`.
4. Commit the output to your repository and open a pull request. Review the diff line-by-line to confirm architectural accuracy.
5. Configure your CI pipeline to auto-regenerate on manifest changes and validate syntax before merge. Add the workflow YAML to `.github/workflows/` and test with a dummy manifest update.
This pipeline transforms documentation from a static liability into a living artifact. By treating diagrams as compiled outputs, engineering teams eliminate decay, accelerate reviews, and maintain architectural clarity across rapid iteration cycles. The tooling is secondary; the workflow discipline is what sustains accuracy.
Mid-Year Sale — Unlock Full Article
Base plan from just $4.99/mo or $49/yr
Sign in to read the full article and unlock all tutorials.
Sign In / Register — Start Free Trial7-day free trial · Cancel anytime · 30-day money-back
