Saltar al contenido

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.

5 min de lectura
Diagrama de jerarquía de documentación mostrando registros de decisiones, runbooks y capas de referencia de API

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

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

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

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.


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
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();
  });
});

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.

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`           |

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.

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

El 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

  1. Escribe ADRs para cada decisión técnica significativa — los ingenieros futuros necesitan el 'porqué', no solo el 'qué'
  2. Crea runbooks para tareas operativas — las guías paso a paso eliminan la toma de decisiones bajo presión
  3. Valida la documentación de API contra la implementación — prueba que las respuestas coincidan con los esquemas de OpenAPI para evitar desviaciones
  4. Mantén los READMEs accionables — responde 'qué, cómo ejecutar, cómo contribuir' en menos de dos minutos
  5. Guarda la documentación junto al código — revísala en PRs, valídala en CI y versionala junto con el código fuente
  6. Automatiza las comprobaciones de frescura — detección de enlaces rotos, validación de esquemas y aplicación de formato en CI
Wilfredo Rujel

Wilfredo Rujel

Ingeniero de Software Full Stack

Compartir esta publicaciónX