Effektive technische Dokumentation für Engineering-Teams
So schreibst du technische Dokumentation, die Entwickler tatsächlich lesen – mit ADRs, Runbooks, API-Dokumentation und Documentation-as-Code-Workflows.

Dokumentation, die niemand liest, ist keine Dokumentation – sie ist verschwendete Arbeit. Die meisten Engineering-Teams haben die falsche Dokumentation: veraltete Wiki-Seiten, ausufernde READMEs und Confluence-Friedhöfe. Das Problem ist nicht, dass Entwickler nicht gerne schreiben. Das Problem ist, dass die meiste Dokumentation verrottet, weil sie von dem Code getrennt ist, den sie beschreibt.
Effektive Dokumentation liegt nah am Code, ist für ein bestimmtes Publikum geschrieben und bleibt durch automatisierte Prüfungen aktuell.
Architektur-Entscheidungsprotokolle (ADRs)
ADRs erfassen das Warum hinter technischen Entscheidungen. In sechs Monaten wird sich niemand mehr daran erinnern, warum du PostgreSQL statt DynamoDB gewählt hast. Der ADR tut es.
## ADR-007: Use PostgreSQL for Order Service
### Status
Accepted (2021-08-15)
### Context
The order service needs a primary data store. Requirements:
- Strong consistency for financial transactions
- Complex queries for reporting (joins, aggregations)
- Up to 10M orders/year with 5-year retention
- Team has existing PostgreSQL expertise
### Options Considered
**PostgreSQL**
- Pros: ACID transactions, strong query language, team expertise
- Cons: Horizontal scaling requires manual sharding
**DynamoDB**
- Pros: Managed scaling, predictable latency at any scale
- Cons: Limited query patterns, eventual consistency by default,
team would need training
**MongoDB**
- Pros: Flexible schema, good for document-shaped data
- Cons: Weaker transaction support, our data is relational
### Decision
PostgreSQL with read replicas for reporting queries.
### Rationale
Our query patterns are relational (orders → items → customers).
10M orders/year fits comfortably on a single PostgreSQL instance
with proper indexing. The team has 4 years of PostgreSQL experience
vs. zero DynamoDB experience. We can revisit if we exceed 100M
orders/year, which is 3+ years out on current growth.
### Consequences
- Need to manage connection pooling (pgBouncer)
- Reporting queries routed to read replica to protect write latency
- Schema migrations managed via FlywayDer ADR beantwortet die Frage, die zukünftige Entwickler stellen werden: 'Warum haben wir das gewählt?'. Speichere ADRs im Repository (docs/adr/), damit sie zusammen mit dem Code weitergegeben werden.
Runbooks
Runbooks sind Schritt-für-Schritt-Anleitungen für operative Aufgaben: Deployments, Incident Response und häufige Debugging-Szenarien. Sie sollten von jemandem ausführbar sein, der das System noch nie gesehen hat.
## Runbook: Database Connection Pool Exhaustion
### Symptoms
- API response times spike above 5 seconds
- Error logs show: `Error: connection pool exhausted`
- Grafana dashboard: `pgbouncer_active_connections` at max
### Diagnosis
1. Check current connection count:
```sql
SELECT count(*) FROM pg_stat_activity
WHERE state = 'active';-
Identify long-running queries:
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE state != 'idle' ORDER BY duration DESC LIMIT 10; -
Check if a specific service is hoarding connections:
SELECT application_name, count(*) FROM pg_stat_activity GROUP BY application_name ORDER BY count DESC;
Resolution
If a single query is blocking:
-- Cancel the query gracefully
SELECT pg_cancel_backend(<pid>);
-- If cancel doesn't work within 30 seconds, terminate
SELECT pg_terminate_backend(<pid>);If a service has too many connections:
- Check the service's pool configuration in
config/database.yml - Verify the replica count hasn't scaled beyond expected
- Restart the affected service:
kubectl rollout restart deployment/<service>
Escalation
If connections remain exhausted after 15 minutes, page the database on-call.
Runbooks funktionieren, weil sie Entscheidungen während Incidents eliminieren. Unter Stress folgen Menschen Checklisten besser, als sie von Grund auf zu schlussfolgern.
## API-Dokumentation
API-Dokumentation hat zwei Zielgruppen: Entwickler, die sich mit deiner API integrieren, und dein zukünftiges Ich, das ein Produktionsproblem debuggt. OpenAPI-Spezifikationen dienen beiden.
```yaml
# ❌ Documentation separate from code — drifts immediately
# docs/api.md
# POST /api/orders - Creates a new order
# Body: { items: [...], customerId: "..." }
# Returns: Order object
# ✅ OpenAPI spec validated against implementation
openapi: '3.0.3'
info:
title: Order Service API
version: '1.0.0'
paths:
/api/v1/orders:
post:
summary: Create a new order
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customerId, items]
properties:
customerId:
type: string
format: uuid
example: "550e8400-e29b-41d4-a716-446655440000"
items:
type: array
minItems: 1
items:
type: object
required: [productId, quantity]
properties:
productId:
type: string
quantity:
type: integer
minimum: 1
responses:
'201':
description: Order created
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Validation error
'401':
description: Not authenticated// Validate API responses match the spec in tests
import SwaggerParser from '@apidevtools/swagger-parser';
describe('Order API', () => {
let spec: any;
beforeAll(async () => {
spec = await SwaggerParser.validate('./openapi.yaml');
});
it('POST /api/v1/orders matches spec', async () => {
const response = await request(app)
.post('/api/v1/orders')
.send({ customerId: 'cust-1', items: [{ productId: 'p1', quantity: 2 }] });
expect(response.status).toBe(201);
// Validate response shape matches OpenAPI schema
const schema = spec.paths['/api/v1/orders'].post.responses['201']
.content['application/json'].schema;
expect(() => validateSchema(response.body, schema)).not.toThrow();
});
});Die API-Spezifikation wird gegen die tatsächliche Implementierung getestet. Wenn sich der Code ändert und die Spezifikation nicht, schlägt der Test fehl. Dokumentation kann nicht auseinanderdriften.
README als Einstieg
Das README ist das erste Dokument, das jemand liest. Es sollte drei Fragen in unter zwei Minuten beantworten: Was ist das, wie starte ich es, wie trage ich bei.
# Order Service
Handles order creation, payment processing, and fulfillment tracking.
Part of the e-commerce platform.
## Quick Start
```bash
# Prerequisites: Node 20, PostgreSQL 14+, Redis 7+
cp .env.example .env
npm install
npm run db:migrate
npm run dev # http://localhost:3000
npm test # Run tests
```
## Architecture
See [docs/adr/](docs/adr/) for decision records.
| Component | Technology | Purpose |
|----------------|-----------|----------------------------|
| API | Express | HTTP endpoints |
| Database | PostgreSQL| Order storage |
| Cache | Redis | Session + rate limiting |
| Queue | BullMQ | Async payment processing |
## API Reference
Full API docs: `npm run docs` → http://localhost:3000/docs
## Common Tasks
| Task | Command |
|-------------------------|--------------------------|
| Run tests | `npm test` |
| Run specific test | `npm test -- --grep "orders"` |
| Generate migration | `npm run db:migration:create` |
| View API docs locally | `npm run docs` |
| Lint | `npm run lint` |Keine Hintergrundabsätze. Keine Installationsanleitung für Git. Geh davon aus, dass der Leser ein Entwickler ist, der die Voraussetzungen aus der Liste ableiten kann. Bring sie so schnell wie möglich zu einem laufenden System.
Dokumentation als Code
Behandle Dokumentation wie Code: Speichere sie in der Versionskontrolle, prüfe sie in Pull Requests und validiere sie in CI.
# .github/workflows/docs.yml
name: Documentation Checks
on: pull_request
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Check for broken links
- name: Link checker
uses: lycheeverse/lychee-action@v1
with:
args: --verbose --no-progress 'docs/**/*.md' 'README.md'
# Validate OpenAPI spec
- name: Validate API spec
run: npx @redocly/cli lint openapi.yaml
# Ensure ADRs follow the template
- name: ADR format check
run: |
for file in docs/adr/adr-*.md; do
if ! grep -q "### Status" "$file"; then
echo "ERROR: $file missing Status section"
exit 1
fi
if ! grep -q "### Decision" "$file"; then
echo "ERROR: $file missing Decision section"
exit 1
fi
doneCI erzwingt Dokumentationsstandards genauso wie Code-Standards. Tote Links, ungültige API-Spezifikationen und fehlerhafte ADRs lassen den Build scheitern.
Wichtige Erkenntnisse
- Schreibe ADRs für jede wichtige technische Entscheidung – zukünftige Entwickler brauchen das 'Warum', nicht nur das 'Was'
- Erstelle Runbooks für operative Aufgaben – Schritt-für-Schritt-Anleitungen eliminieren Entscheidungen unter Stress
- Validiere API-Dokumentation gegen die Implementierung – teste, dass Antworten mit OpenAPI-Schemas übereinstimmen, um Drift zu vermeiden
- Halte READMEs aktionsorientiert – beantworte 'Was, wie starten, wie beitragen' in unter zwei Minuten
- Bewahre Dokumentation neben dem Code auf – prüfe sie in PRs, validiere sie in CI und versioniere sie zusammen mit dem Quellcode
- Automatisiere Frischeprüfungen – Erkennung toter Links, Schema-Validierung und Format-Erzwingung in CI


