Documentación técnica efectiva para equipos de ingeniería
Cómo escribir documentación técnica que los ingenieros realmente lean — con ADRs, runbooks, documentación de API y flujos de documentación como código.

La documentación que nadie lee no es documentación: es esfuerzo desperdiciado. La mayoría de equipos de ingeniería tienen la documentación equivocada: páginas de wiki obsoletas, READMEs interminables y cementerios en Confluence. El problema no es que a los ingenieros no les guste escribir. El problema es que la mayor parte de la documentación se pudre porque está desconectada del código que describe.
La documentación efectiva vive cerca del código, está escrita para una audiencia específica y se mantiene actualizada mediante verificaciones automatizadas.
Registros de decisiones de arquitectura (ADRs)
Los ADRs capturan el porqué detrás de las decisiones técnicas. Dentro de seis meses, nadie recordará por qué elegiste PostgreSQL en lugar de DynamoDB. El ADR sí.
## 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 FlywayEl ADR responde a la pregunta que los ingenieros futuros se harán: '¿Por qué elegimos esto?'. Guarda los ADRs en el repositorio (docs/adr/) para que viajen junto con el código.
Runbooks
Los runbooks son guías paso a paso para tareas operativas: despliegues, respuesta a incidentes y escenarios comunes de depuración. Deberían poder ejecutarlos alguien que nunca haya visto el sistema antes.
## 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.
Los runbooks funcionan porque eliminan la toma de decisiones durante los incidentes. Bajo presión, la gente sigue listas de verificación mejor de lo que razona desde primeros principios.
## Documentación de API
La documentación de API tiene dos audiencias: los desarrolladores que se integran con tu API y tu yo futuro depurando un problema en producción. Las especificaciones de OpenAPI sirven para ambos.
```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();
});
});La especificación de API se prueba contra la implementación real. Cuando el código cambia y la especificación no, la prueba falla. La documentación no puede desviarse.
README como punto de entrada
El README es el primer documento que cualquiera lee. Debería responder tres preguntas en menos de dos minutos: qué es esto, cómo lo ejecuto, cómo contribuyo.
# 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` |Sin párrafos de contexto. Sin guía de instalación de Git. Asume que el lector es un ingeniero que puede deducir los requisitos previos de la lista. Llévalos a un sistema funcionando lo más rápido posible.
Documentación como código
Trata la documentación como código: guárdala en control de versiones, revísala en pull requests y valídala en 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
doneEl CI hace cumplir los estándares de documentación de la misma manera que los de código. Los enlaces rotos, las especificaciones de API inválidas y los ADRs mal formados hacen fallar el build.
Conclusiones clave
- Escribe ADRs para cada decisión técnica significativa — los ingenieros futuros necesitan el 'porqué', no solo el 'qué'
- Crea runbooks para tareas operativas — las guías paso a paso eliminan la toma de decisiones bajo presión
- Valida la documentación de API contra la implementación — prueba que las respuestas coincidan con los esquemas de OpenAPI para evitar desviaciones
- Mantén los READMEs accionables — responde 'qué, cómo ejecutar, cómo contribuir' en menos de dos minutos
- Guarda la documentación junto al código — revísala en PRs, valídala en CI y versionala junto con el código fuente
- Automatiza las comprobaciones de frescura — detección de enlaces rotos, validación de esquemas y aplicación de formato en CI


