Zum Inhalt springen

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.

5 Min. Lesezeit
Dokumentationshierarchie-Diagramm mit Decision Records, Runbooks und API-Referenz-Ebenen

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.

markdownmarkdown
## 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 Flyway

Der 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.

markdownmarkdown
## 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';
  1. Identify long-running queries:

    sqlsql
    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;
  2. Check if a specific service is hoarding connections:

    sqlsql
    SELECT application_name, count(*)
    FROM pg_stat_activity
    GROUP BY application_name
    ORDER BY count DESC;

Resolution

If a single query is blocking:

sqlsql
-- 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:

  1. Check the service's pool configuration in config/database.yml
  2. Verify the replica count hasn't scaled beyond expected
  3. 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
ymlyaml
# ✅ 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
tstypescript
// 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.

markdownmarkdown
# 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.

ymlyaml
# .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
          done

CI erzwingt Dokumentationsstandards genauso wie Code-Standards. Tote Links, ungültige API-Spezifikationen und fehlerhafte ADRs lassen den Build scheitern.

Wichtige Erkenntnisse

  1. Schreibe ADRs für jede wichtige technische Entscheidung – zukünftige Entwickler brauchen das 'Warum', nicht nur das 'Was'
  2. Erstelle Runbooks für operative Aufgaben – Schritt-für-Schritt-Anleitungen eliminieren Entscheidungen unter Stress
  3. Validiere API-Dokumentation gegen die Implementierung – teste, dass Antworten mit OpenAPI-Schemas übereinstimmen, um Drift zu vermeiden
  4. Halte READMEs aktionsorientiert – beantworte 'Was, wie starten, wie beitragen' in unter zwei Minuten
  5. Bewahre Dokumentation neben dem Code auf – prüfe sie in PRs, validiere sie in CI und versioniere sie zusammen mit dem Quellcode
  6. Automatisiere Frischeprüfungen – Erkennung toter Links, Schema-Validierung und Format-Erzwingung in CI
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX