El problema de volver a tu propio código
Casi todos los desarrolladores han vivido la misma escena: abres un proyecto que dejaste a medias hace tres meses, miras el código y no reconoces nada. No recuerdas por qué elegiste esa librería, qué comando arrancaba el entorno local, qué variable de entorno faltaba para que funcionaran las pruebas ni por qué hay una función con un nombre extraño que nadie se atreve a borrar. El código está ahí, pero el contexto que lo explicaba se ha evaporado.
Ese contexto perdido es exactamente lo que la documentación debería preservar. Y aquí aparece una idea que cambia la forma de abordar el problema: la primera persona que se beneficia de la documentación no es un usuario anónimo ni un compañero nuevo, sino tu yo del futuro. Si escribes pensando en esa persona, que conoce el dominio pero ha olvidado los detalles, el resultado será útil también para el resto del equipo.
Esta guía propone un método en tres pasos para documentar mientras trabajas, sin convertirlo en una tarea pesada que se pospone eternamente: tomar notas precisas durante el desarrollo, explicar las decisiones con calma poco después y completar el conocimiento previo que el lector necesitará. A partir de ahí, verás cómo convertir esas notas en artefactos concretos y duraderos: un README que se entiende en cinco minutos, registros de decisiones de arquitectura, comentarios que explican el porqué, un Makefile que se documenta solo y un sitio de documentación construido con herramientas actuales.
El objetivo no es escribir más, sino escribir lo que realmente evita perder horas: lo que no se puede deducir leyendo el código.
Primer paso: tomar notas mientras trabajas
La documentación más fiable es la que se escribe en el momento en que ocurre el trabajo. Cuando terminas una tarea, la memoria ya ha empezado a reconstruir los hechos de forma selectiva: olvidas los callejones sin salida, los comandos que fallaron y el orden real en que hiciste las cosas. Por eso el primer hábito es mantener un archivo de notas abierto junto al editor y registrar lo que haces a medida que lo haces.
No hace falta escribir con cuidado. Frases incompletas, fragmentos de comandos y enlaces sueltos son perfectamente válidos en esta fase. Lo importante es capturar cuatro tipos de información:
- Los comandos que ejecutas en la terminal, incluidos los que fallan y el error que producen.
- Las razones por las que eliges una opción frente a otra, aunque sea en una línea.
- Los enlaces a documentación, incidencias o respuestas que te ayudaron.
- El orden de los pasos, porque reproducir un procedimiento casi siempre depende de él.
Un archivo de notas de trabajo podría tener este aspecto:
# Notas: migración de sesiones a Redis (2026-10-28)
- Objetivo: sacar las sesiones de la memoria del proceso para poder escalar a 3 réplicas
- `docker compose up redis` → arranca, pero la app no conecta
- error: ECONNREFUSED 127.0.0.1:6379 → dentro del contenedor localhost no es el host
- solución: usar `REDIS_URL=redis://redis:6379` (nombre del servicio)
- Elegí connect-redis en vez de guardar sesiones en Postgres:
- menos carga en la BD principal, TTL nativo
- Las sesiones antiguas se pierden en el despliegue → avisar en el canal de soporte
- Ver: incidencia #412 sobre cookies `SameSite` en el subdominio de pagos
- Orden real: 1) servicio redis en compose 2) variable de entorno 3) middleware 4) tests
Activa el guardado automático del editor para no perder nada y guarda estas notas en el propio repositorio, en una carpeta que no se publique, o en tu sistema personal de notas. Lo relevante es que existan y que tengan fecha.
Segundo paso: explicar las decisiones con calma
Las notas en bruto son un material excelente, pero no son documentación. El segundo paso consiste en volver sobre ellas poco después, idealmente tras una pausa breve pero todavía con el contexto fresco, y convertir los apuntes en explicaciones completas.
Una técnica útil es la del pato de goma: explicar el trabajo como si se lo contaras a un compañero que no estuvo presente. Al obligarte a verbalizar el razonamiento, descubres huecos que pasaban desapercibidos y decisiones que parecían obvias pero no lo son. Las preguntas que guían esta fase son siempre las mismas:
- Qué decisiones poco habituales tomaste y por qué.
- Qué problemas encontraste y cómo los resolviste.
- Cómo encaja la solución con los objetivos generales del proyecto.
No se trata de narrar cada paso, sino de destacar lo que alguien no podría deducir leyendo el código. Si una línea es evidente, no necesita explicación; si una configuración parece extraña y tiene una buena razón detrás, esa razón debe quedar escrita.
El lugar natural para estas explicaciones depende de su alcance. Las que afectan a una función concreta van en un comentario junto al código. Las que afectan a cómo se usa o se despliega el proyecto van en el README o en la guía correspondiente. Y las que afectan a la arquitectura merecen un formato propio: los registros de decisiones, que veremos más adelante.
Tercer paso: no dar por supuesto el conocimiento previo
Al día siguiente, relee lo que escribiste con ojos de lector. Es el momento de detectar todo lo que diste por sabido. Una frase como “configura las credenciales y lanza las migraciones” es clara para quien acaba de hacerlo, pero opaca para quien llega de nuevo: qué credenciales, dónde se configuran, con qué comando se lanzan las migraciones, qué hacer si fallan.
En esta fase conviene enlazar el material de apoyo en lugar de reescribirlo: la documentación oficial de las APIs que usas, los pasos de instalación de una dependencia, la incidencia de GitHub donde se discutió un problema o el README de otro repositorio del que depende el tuyo. Enlazar evita que tu documentación quede desactualizada cuando cambie la fuente original.
Una pregunta útil para esta revisión es: ¿podría una persona competente, que no conoce este proyecto, ponerlo en marcha solo con lo que hay escrito? Si la respuesta es no, falta información. Una forma de comprobarlo de verdad es pedir a alguien del equipo que siga las instrucciones en un equipo limpio y anote cada punto en el que se atasca. Cada atasco es una línea de documentación pendiente.
Un README que se entiende en cinco minutos
El README es la puerta de entrada del proyecto y, en muchos casos, el único documento que la gente llega a leer. Su trabajo es responder rápido a las preguntas básicas: qué es esto, cómo lo pongo en marcha, cómo lo uso y dónde encuentro más información. Una buena estructura de un README sigue casi siempre el mismo esquema:
# Facturador
Servicio que genera facturas en PDF a partir de pedidos y las envía por correo.
## Requisitos
- Node.js 24 o superior
- Docker y Docker Compose
- Una cuenta de pruebas del proveedor de correo
## Puesta en marcha
1. Copia la configuración de ejemplo: `cp .env.example .env`
2. Arranca los servicios auxiliares: `docker compose up -d`
3. Instala dependencias y aplica migraciones: `make setup`
4. Inicia el servidor: `make dev`
La API queda disponible en http://localhost:3000.
## Uso
Genera una factura de prueba:
curl -X POST localhost:3000/facturas -d '{"pedido": 1042}'
## Pruebas
`make test` ejecuta las pruebas unitarias y de integración.
## Documentación adicional
- Arquitectura y decisiones: `docs/decisiones/`
- Referencia de la API: `docs/api/openapi.yaml`
- Guía de despliegue: `docs/despliegue.md`
## Licencia
MIT
Algunas pautas hacen que un README funcione mejor. La primera línea debe explicar qué hace el proyecto en una sola frase, sin jerga interna. Los pasos de instalación deben poder copiarse y pegarse tal cual, en orden. Los requisitos deben indicar versiones mínimas cuando importan. Y el README no debe intentar contenerlo todo: cuando una sección crece demasiado, es mejor moverla a un documento propio y enlazarla.
Registrar las decisiones de arquitectura
Las decisiones importantes de un proyecto, como elegir una base de datos, adoptar una arquitectura de eventos o cambiar el sistema de autenticación, suelen discutirse en reuniones, hilos de chat o comentarios de pull requests. Meses después, esa discusión es imposible de encontrar y la decisión parece arbitraria. Los registros de decisiones de arquitectura (ADR, por sus siglas en inglés) resuelven este problema con un formato breve y fijo.
Cada ADR es un archivo Markdown numerado que documenta una única decisión: el contexto que la motivó, la decisión tomada y sus consecuencias. Se guardan en el repositorio, junto al código, y nunca se borran: si una decisión se revierte, se escribe un nuevo ADR que reemplaza al anterior y se actualiza el estado del antiguo.
# ADR 0007: Almacenar sesiones en Redis
- Estado: aceptado
- Fecha: 2026-10-28
- Reemplaza a: ADR 0002
## Contexto
Las sesiones se guardan en la memoria del proceso. Para escalar el servicio
a varias réplicas detrás del balanceador, cada petición debe poder atenderse
en cualquier instancia.
## Decisión
Usaremos Redis como almacén de sesiones, con un TTL de 7 días, desplegado
como servicio gestionado en el mismo proveedor que la aplicación.
## Alternativas consideradas
- Sesiones en PostgreSQL: descartado por añadir carga de escritura a la BD principal.
- Afinidad de sesión en el balanceador: descartado porque complica los despliegues
y pierde sesiones al reiniciar una réplica.
## Consecuencias
- Las sesiones existentes se perderán en el despliegue de la migración.
- Se añade una dependencia de infraestructura que hay que monitorizar.
- Permite escalar horizontalmente sin cambios adicionales en la aplicación.
Los registros de decisiones arquitectónicas tienen un valor que crece con el tiempo. Un desarrollador que se incorpora puede leer en una tarde la historia de por qué el sistema es como es, y el equipo evita reabrir debates que ya se resolvieron con buenos argumentos.
La estructura de carpetas habitual mantiene todo junto:
docs/
├── decisiones/
│ ├── 0001-usar-postgresql.md
│ ├── 0002-sesiones-en-memoria.md
│ ├── 0007-sesiones-en-redis.md
│ └── plantilla.md
├── api/
│ └── openapi.yaml
├── despliegue.md
└── index.md
Comentarios que explican el porqué
Los comentarios en el código son la documentación más cercana a lo que describen, y por eso la más fácil de mantener actualizada, siempre que expliquen lo correcto. La regla más útil es sencilla: el código ya dice qué hace; el comentario debe decir por qué lo hace así.
Este comentario no aporta nada, porque repite lo que cualquiera puede leer:
# Incrementa el contador en 1
reintentos += 1
Este, en cambio, contiene información que el código no puede expresar:
# El proveedor de pagos devuelve 409 durante unos segundos tras crear un cliente
# mientras replica el registro entre regiones. Reintentamos hasta 3 veces con
# espera creciente en lugar de fallar. Ver incidencia #388.
for intento in range(3):
respuesta = cliente_pagos.crear_cargo(pedido)
if respuesta.status_code != 409:
break
time.sleep(2 ** intento)
Para las funciones y clases públicas, los comentarios de documentación estructurados (docstrings en Python, JSDoc en JavaScript, comentarios de documentación en Rust o Go) cumplen una doble función: explican la interfaz a quien lee el código y alimentan el autocompletado del editor y los generadores de referencia.
def calcular_descuento(total: float, cliente: Cliente) -> float:
"""Calcula el descuento aplicable a un pedido.
Los clientes con más de 12 meses de antigüedad reciben un 5 %, y los
pedidos superiores a 500 € suman un 3 % adicional. El descuento nunca
supera el 10 % por acuerdo con el departamento comercial.
Args:
total: Importe del pedido antes de impuestos, en euros.
cliente: Cliente que realiza el pedido.
Returns:
Importe del descuento en euros, redondeado a dos decimales.
"""
/**
* Convierte un importe a la moneda indicada usando la tasa del día.
*
* Las tasas se cachean durante una hora para no superar el límite
* de peticiones del proveedor de cambio.
*
* @param {number} importe - Cantidad en la moneda de origen.
* @param {string} destino - Código ISO 4217 de la moneda de destino.
* @returns {Promise<number>} Importe convertido con dos decimales.
* @throws {Error} Si la moneda de destino no está soportada.
*/
export async function convertirMoneda(importe, destino) {
// ...
}
Fíjate en que ambos ejemplos documentan reglas de negocio y restricciones externas, no la mecánica del código. Eso es lo que se olvida y lo que más cuesta reconstruir.
Un Makefile que se documenta solo
Cada proyecto acumula comandos que nadie recuerda: cómo arrancar el entorno, cómo regenerar los datos de prueba, cómo lanzar solo las pruebas de integración, cómo construir la imagen de producción. En lugar de buscarlos cada vez en el historial de la terminal o en una página del manual, se pueden reunir en un Makefile con un objetivo de ayuda que lista todos los comandos disponibles a partir de sus propios comentarios.
El truco consiste en añadir un comentario con doble almohadilla al final de cada objetivo y un objetivo help que los extrae:
.DEFAULT_GOAL := help
.PHONY: help setup dev test lint docs build
help: ## Muestra esta ayuda
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-12s\033[0m %s\n", $$1, $$2}'
setup: ## Instala dependencias y aplica migraciones
npm ci
npm run db:migrate
dev: ## Arranca el servidor en modo desarrollo
docker compose up -d
npm run dev
test: ## Ejecuta pruebas unitarias y de integración
npm test
lint: ## Revisa el código y la documentación
npx eslint .
npx markdownlint-cli2 "**/*.md" "#node_modules"
docs: ## Sirve el sitio de documentación en local
npm run docs:dev
build: ## Construye la imagen de producción
docker build -t facturador:latest .
Al ejecutar make sin argumentos, aparece la lista de comandos con su descripción:
make
help Muestra esta ayuda
setup Instala dependencias y aplica migraciones
dev Arranca el servidor en modo desarrollo
test Ejecuta pruebas unitarias y de integración
lint Revisa el código y la documentación
docs Sirve el sitio de documentación en local
build Construye la imagen de producción
Un Makefile autodocumentado con ayuda integrada tiene una ventaja importante frente a una lista de comandos en el README: la documentación y el comando viven en la misma línea, así que es difícil que se desincronicen. Recuerda que las recetas de un Makefile deben indentarse con tabuladores, no con espacios; es el error más común al escribir uno por primera vez.
Los cuatro tipos de documentación
No toda la documentación cumple la misma función, y mezclar propósitos es una de las causas más frecuentes de documentos confusos. El marco Diátaxis, muy adoptado en proyectos de código abierto, propone distinguir cuatro tipos según lo que necesita el lector:
| Tipo | Pregunta que responde | Ejemplo |
|---|---|---|
| Tutorial | ¿Cómo empiezo? | Construir una primera integración paso a paso |
| Guía práctica | ¿Cómo hago esta tarea concreta? | Cómo rotar las claves de la API |
| Referencia | ¿Qué hace exactamente esto? | Lista de endpoints y parámetros |
| Explicación | ¿Por qué funciona así? | Diseño del sistema de colas |
Un tutorial guía de la mano a alguien que empieza, con un resultado garantizado. Una guía práctica asume que el lector ya sabe lo básico y quiere resolver un problema concreto. La referencia es exhaustiva, seca y precisa, idealmente generada a partir del código. La explicación aporta contexto, historia y razonamiento, y es donde encajan los registros de decisiones.
Separar estos tipos ayuda a escribir mejor cada uno: el tutorial no se llena de digresiones, la referencia no intenta enseñar y la explicación no se pierde en pasos operativos.
Documentación de APIs a partir de una especificación
Para las APIs HTTP, la referencia no debería escribirse a mano. La especificación OpenAPI describe endpoints, parámetros, respuestas y errores en un formato estructurado a partir del cual se generan páginas de referencia interactivas, clientes y pruebas de contrato. Mantenerla en el repositorio convierte la documentación de la API en un artefacto verificable:
# docs/api/openapi.yaml
openapi: 3.1.0
info:
title: API del facturador
version: 1.4.0
paths:
/facturas:
post:
summary: Genera una factura para un pedido existente
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [pedido]
properties:
pedido:
type: integer
description: Identificador del pedido a facturar
responses:
"201":
description: Factura creada; devuelve la URL del PDF
"404":
description: El pedido no existe
"409":
description: El pedido ya tiene una factura asociada
Muchos frameworks generan esta especificación automáticamente a partir del código, lo que elimina por completo el riesgo de que referencia e implementación diverjan. Si se escribe a mano, conviene validarla en la integración continua.
Documentación como código y herramientas actuales
El enfoque de documentación como código trata los documentos igual que el software: se escriben en texto plano (normalmente Markdown), se guardan en el mismo repositorio, se revisan en pull requests, se validan automáticamente y se publican con cada cambio. Sus ventajas son claras: la documentación evoluciona junto al código, cualquier cambio queda registrado en el historial y las revisiones permiten detectar errores antes de publicarlos.
Cuando el proyecto necesita un sitio de documentación navegable, hay varios generadores consolidados. Docusaurus, mantenido por Meta, construye sitios con React y ofrece versionado, búsqueda e internacionalización; su versión estable actual es la serie 3.10. Crear un sitio nuevo es directo:
npx create-docusaurus@latest docs-facturador classic
cd docs-facturador
npm start
En el ecosistema Python, MkDocs con el tema Material fue durante años la opción más popular. Conviene saber que Material for MkDocs entró en modo de mantenimiento a principios de 2026: sigue recibiendo correcciones críticas y de seguridad, pero el desarrollo de nuevas funciones se ha trasladado a Zensical, un generador del mismo equipo diseñado para leer las configuraciones mkdocs.yml existentes. Para un proyecto nuevo merece la pena evaluar Zensical; para uno existente, la compatibilidad con la configuración facilita una migración gradual. Un archivo de configuración típico tiene este aspecto:
# mkdocs.yml
site_name: Documentación del facturador
nav:
- Inicio: index.md
- Puesta en marcha: puesta-en-marcha.md
- Guías:
- Despliegue: despliegue.md
- Rotación de claves: rotacion-claves.md
- Decisiones: decisiones/
theme:
name: material
language: es
Otras alternativas habituales son Starlight (basado en Astro), VitePress (del ecosistema Vue), Sphinx (muy extendido en proyectos científicos de Python) y el propio Hugo. La elección depende sobre todo del ecosistema del equipo: lo importante es que el contenido esté en Markdown, porque así es portable entre herramientas.
Revisar la documentación automáticamente
Si la documentación vive en el repositorio, puede validarse en cada cambio igual que el código. Un linter de Markdown detecta errores de formato, encabezados mal anidados o listas inconsistentes. Un corrector de estilo como Vale aplica las reglas de redacción del equipo, por ejemplo evitar términos ambiguos o mantener una terminología uniforme. Y un comprobador de enlaces evita publicar referencias rotas.
Una configuración mínima de markdownlint en la raíz del proyecto:
{
"default": true,
"MD013": false,
"MD033": { "allowed_elements": ["details", "summary"] }
}
Y un flujo de trabajo de GitHub Actions que revisa la documentación en cada pull request:
# .github/workflows/docs.yml
name: Documentación
on:
pull_request:
paths:
- "**/*.md"
- "docs/**"
jobs:
revisar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: DavidAnson/markdownlint-cli2-action@v24
with:
globs: "**/*.md"
Automatizar estas comprobaciones tiene un efecto cultural además del técnico: comunica que la documentación es parte del trabajo, con el mismo nivel de exigencia que el código.
Errores comunes al documentar
Hay patrones que aparecen una y otra vez y que convierten la documentación en un estorbo en lugar de una ayuda.
Documentar al final. Escribir la documentación cuando el proyecto está terminado garantiza que se hará con prisa y sin contexto. El método de tres pasos existe precisamente para documentar durante el trabajo.
Duplicar información. Si los mismos pasos de instalación aparecen en el README, en la wiki y en un documento de incorporación, tarde o temprano dejarán de coincidir. Cada dato debe tener un único lugar canónico, y el resto de documentos deben enlazarlo.
Explicar el qué en lugar del porqué. Los comentarios que parafrasean el código añaden ruido y se desactualizan con facilidad. Reserva el texto para lo que el código no puede expresar.
Escribir para uno mismo del presente. Lo que hoy es obvio no lo será en seis meses. Releer al día siguiente, como propone el tercer paso, es la defensa más sencilla contra esta trampa.
Dejar documentación obsoleta. Un documento incorrecto es peor que ninguno, porque transmite confianza falsa. Incluye la revisión de la documentación en la definición de terminado de cada tarea y elimina sin miedo lo que ya no aplica.
Esconder la documentación. Si nadie sabe dónde está, no existe. Enlaza todo desde el README y mantén una estructura predecible.
Documentar más allá del código
El software no es solo código, y la documentación tampoco debería limitarse a él. Hay otros elementos de un proyecto que se benefician enormemente de quedar por escrito.
Los procesos operativos, como desplegar, revertir una versión o responder a una caída, deben tener guías de actuación (runbooks) que cualquier miembro del equipo pueda seguir bajo presión, a las tres de la madrugada, sin depender de quien lo hizo la última vez.
La infraestructura definida como código, con herramientas como Terraform o Ansible, es en sí misma una forma de documentación ejecutable, pero necesita un contexto que explique qué recursos existen, por qué están configurados así y qué dependencias tienen entre sí.
El historial de cambios orientado a los usuarios, en un archivo CHANGELOG.md, permite saber qué cambió en cada versión sin leer los commits uno por uno:
## [1.4.0] - 2026-10-30
### Añadido
- Envío de facturas en formato XML para clientes empresariales.
### Cambiado
- Las sesiones se almacenan ahora en Redis (ver ADR 0007).
### Corregido
- El PDF ya no corta las direcciones con más de tres líneas.
Incluso las ideas que todavía no se han convertido en trabajo, como posibles mejoras, deudas técnicas detectadas o funciones que se descartaron, merecen una nota breve. Muchas veces la pregunta más difícil de responder en un proyecto es por qué algo no se hizo, y una línea escrita en su momento ahorra una investigación completa.
Documentación y asistentes de IA
Los asistentes de código basados en IA se han convertido en una herramienta habitual, y su relación con la documentación funciona en ambas direcciones. Por un lado, pueden ayudar a redactar borradores de docstrings, resumir cambios para un changelog o detectar secciones del README que han quedado desactualizadas respecto al código. Por otro, la calidad de sus respuestas sobre tu proyecto depende en gran medida de la documentación que encuentren: un repositorio con un README claro, decisiones registradas y comentarios que explican el porqué produce sugerencias mucho más acertadas.
Lo que un asistente no puede hacer es reconstruir el razonamiento que nunca se escribió. Las decisiones, los compromisos con otros equipos y las restricciones externas solo existen si alguien las dejó por escrito. Usa la IA para acelerar la redacción, pero revisa siempre lo que genera: una documentación plausible pero incorrecta es especialmente dañina.
Cómo saber si tu documentación funciona
Medir la utilidad de la documentación es más difícil que medir la cobertura de pruebas, pero hay señales prácticas que indican si está cumpliendo su función.
La primera es el tiempo de incorporación: cuánto tarda una persona nueva en tener el proyecto funcionando en su equipo y en hacer su primera contribución. Si ese tiempo baja después de mejorar el README y las guías, la documentación está funcionando. Pide a cada incorporación que anote los puntos donde se atascó y conviértelos en mejoras concretas.
La segunda son las preguntas repetidas. Si la misma duda aparece una y otra vez en el chat del equipo, falta un documento o el que existe no se encuentra. Responder con un enlace en lugar de con una explicación nueva es la mejor señal de que la documentación ya cubre ese caso.
La tercera es la frescura. Revisa de vez en cuando la fecha del último cambio de cada documento importante y compárala con la del código que describe. Una guía de despliegue que no se ha tocado en dos años mientras la infraestructura ha cambiado varias veces es, casi con seguridad, incorrecta.
Conclusiones
La buena documentación no nace de sesiones heroicas al final de un proyecto, sino de un hábito sencillo integrado en el trabajo diario. Tomar notas precisas mientras desarrollas, explicar las decisiones con calma poco después y completar al día siguiente el conocimiento previo que falta son tres pasos que requieren poco esfuerzo y protegen el contexto que el código, por sí solo, no puede conservar.
Esas notas se convierten después en artefactos concretos: un README que permite arrancar el proyecto en minutos, registros de decisiones que explican por qué el sistema es como es, comentarios centrados en el porqué, un Makefile que lista sus propios comandos y una referencia de API generada a partir de una especificación. Tratar todo ello como código, en el mismo repositorio, revisado y validado automáticamente, mantiene la documentación viva junto al software.
Las herramientas evolucionan, como muestra el paso de Material for MkDocs a modo de mantenimiento o la consolidación de Docusaurus 3.10, pero el principio de fondo no cambia: escribe primero para tu yo del futuro. Si esa persona puede retomar el proyecto sin perder una tarde reconstruyendo lo que ya sabías, habrás escrito una documentación de software efectiva para todo el equipo.
