Software Architecture Documentation: C4 Model, ADRs, and Living Docs
Software Architecture Documentation That People Actually Read#
Most architecture docs are written once and never updated. They rot in Confluence while the system evolves. Here's how to create documentation that stays useful.
The Documentation Problem#
What usually happens:
- Architect draws diagrams in Lucidchart
- Writes a 40-page doc in Confluence
- Team reads it once during onboarding
- System changes, doc doesn't
- New engineer asks "is this still accurate?" → nobody knows
What should happen: Documentation that's close to the code, easy to update, and automatically validated.
The C4 Model#
Simon Brown's C4 model gives you four zoom levels:
Level 1: System Context#
Who uses the system and what external systems does it interact with?
[User] → [Your System] → [Payment Provider]
→ [Email Service]
→ [Analytics]
Audience: Everyone — business stakeholders, new engineers, PMs.
Level 2: Container#
What are the major deployable units (apps, databases, queues)?
[Web App (React)] → [API Server (Node.js)] → [PostgreSQL]
→ [Redis]
→ [Kafka]
→ [Worker (Python)] → [ML Model]
Audience: Engineers, DevOps, architects.
Level 3: Component#
What are the major components inside each container?
API Server:
├── Auth Controller → Auth Service → User Repository
├── Order Controller → Order Service → Order Repository
└── Payment Controller → Payment Service → Stripe Client
Audience: Engineers working on that container.
Level 4: Code#
Class diagrams, function signatures. Usually auto-generated.
Audience: Engineers debugging specific code.
Rule: Most teams only need Levels 1-3. Level 4 is auto-generated or skipped.
Architecture Decision Records (ADRs)#
Document why decisions were made, not just what was built.
ADR Template#
# ADR-001: Use PostgreSQL over MongoDB
## Status: Accepted
## Context
We need a primary database for user and order data.
Our data is relational (users have orders, orders have items).
Team has strong SQL experience.
## Decision
Use PostgreSQL 16 on AWS RDS.
## Consequences
- Strong consistency and ACID transactions
- Need to manage migrations (using Prisma)
- Limited horizontal scaling (read replicas for now)
- Rejected MongoDB because our data is relational
Where to Store ADRs#
docs/
adr/
001-use-postgresql.md
002-event-driven-over-sync.md
003-kubernetes-over-ecs.md
004-jwt-over-sessions.md
In the repo, not in Confluence. ADRs should be reviewed in PRs just like code.
README-Driven Documentation#
Your README is the most-read document in your project:
# Project Name
## Architecture
[Link to interactive diagram on Codelit.io]
## Quick Start
1. Clone → 2. Install → 3. Run
## Services
- **api/** — REST API (Node.js, port 3000)
- **worker/** — Background jobs (Python)
- **web/** — Frontend (Next.js, port 8080)
## Key Decisions
- [ADR-001: PostgreSQL over MongoDB](docs/adr/001-use-postgresql.md)
- [ADR-002: Event-driven architecture](docs/adr/002-event-driven.md)
## Deployment
- Production: Vercel (frontend) + AWS ECS (API)
- CI/CD: GitHub Actions
Living Documentation#
Diagrams as Code#
Generate diagrams from code so they can't go stale:
# Diagrams (Python library)
from diagrams import Diagram
from diagrams.aws.compute import ECS
from diagrams.aws.database import RDS
with Diagram("Production"):
ECS("API") >> RDS("PostgreSQL")
Tools: Mermaid (in Markdown), Structurizr (C4), Diagrams (Python), Codelit.io (AI-generated)
Auto-Generated API Docs#
- OpenAPI/Swagger — generated from code annotations
- GraphQL introspection — schema IS the documentation
- tRPC — TypeScript types ARE the documentation
Architecture Fitness Functions#
Automated tests that verify architecture rules:
// ArchUnit-style test
test("controllers should not depend on repositories directly", () => {
const violations = findImports("src/controllers/**")
.filter(imp => imp.includes("/repositories/"));
expect(violations).toHaveLength(0);
});
Documentation Checklist#
| Document | Where | Update Frequency |
|---|---|---|
| System context diagram | README | Quarterly |
| Container diagram | README or /docs | Monthly |
| ADRs | /docs/adr/ | Per decision |
| API docs | Auto-generated | Every deploy |
| Runbooks | /docs/runbooks/ | Per incident |
| Onboarding guide | /docs/onboarding.md | Quarterly |
Common Mistakes#
- Too much detail — nobody reads 40-page docs
- Wrong audience — C4 Level 3 for a business stakeholder
- Separate from code — Confluence docs rot; in-repo docs get reviewed
- No "why" — ADRs capture reasoning, not just decisions
- Static diagrams — use interactive/generated diagrams that update
Summary#
- C4 model for layered architecture views (context → container → component)
- ADRs for decision history ("why PostgreSQL?")
- README as the entry point — quick start, services, key decisions
- Diagrams as code or AI-generated so they don't go stale
- Auto-generate API docs from code (OpenAPI, GraphQL schema)
- Keep docs in the repo — reviewed in PRs, versioned with code
Generate interactive architecture diagrams at codelit.io — describe any system, export as Mermaid, share live links, embed in docs.
108 articles on system design. Browse the full curriculum at codelit.io/blog.
Try it on Codelit
Chaos Mode
Simulate node failures and watch cascading impact across your architecture
Related articles
AI Agent Tool Use Architecture: Function Calling, ReAct Loops & Structured Outputs
6 min read
AI searchAI-Powered Search Architecture: Semantic Search, Hybrid Search, and RAG
8 min read
AI safetyAI Safety Guardrails Architecture: Input Validation, Output Filtering, and Human-in-the-Loop
8 min read
Try these templates
Netflix Video Streaming Architecture
Global video streaming platform with adaptive bitrate, CDN distribution, and recommendation engine.
10 componentsSearch Engine Architecture
Web-scale search with crawling, indexing, ranking, and sub-second query serving.
8 componentsGoogle Search Engine Architecture
Web-scale search with crawling, indexing, PageRank, query processing, ads, and knowledge graph.
10 componentsBuild this architecture
Generate an interactive Software Architecture Documentation in seconds.
Try it in Codelit →
Comments