ADRs: Documenta Tus Decisiones Arquitectónicas (o Paga el Precio)

“¿Por qué usamos Redis en vez de Memcached para las sesiones?”

Silencio. Nadie se acuerda. El que lo decidió se fue hace cuatro meses. El commit dice feat: add Redis session handler pero no dice por qué Redis y no Memcached. No dice que evaluamos tres alternativas. No dice que Memcached no soportaba el tipo de estructura que necesitábamos para los datos de sesión con claims de permisos anidados.

He vivido esta escena decenas de veces en 15 años gestionando equipos. Y siempre acaba igual: alguien decide que “mejor lo cambiamos” porque no entiende el contexto original, y seis semanas después descubre por las malas por qué se tomó esa decisión.

El código explica el qué. Los commits explican el cuándo. Pero nadie explica el por qué. Y el “por qué” es lo que te ahorra meses.

Qué es un ADR

Un ADR — Architecture Decision Record — es un documento corto que captura una decisión arquitectónica junto con su contexto, las alternativas que se evaluaron y las consecuencias de la elección. Martin Fowler lo define como “short documents that capture a decision, structured into logs that reveal a history of architectural thinking” (fuente).

No es documentación. La documentación describe el sistema como es. Un ADR captura un momento en el tiempo: teníamos este problema, evaluamos estas opciones, y elegimos esta por estas razones. Es un registro histórico de pensamiento arquitectónico.

La diferencia es clave. La documentación descriptiva tiende a quedarse obsoleta porque el sistema cambia y nadie actualiza el documento. Un ADR tiene una ventaja inherente: captura un momento en el tiempo, así que no necesita actualizarse. Si la decisión cambia, no modificas el ADR original — creas uno nuevo que lo reemplaza. Eso sí: necesitas marcar los que ya no aplican, o acabas con un directorio que confunde más de lo que ayuda.

La estructura de un ADR

Un ADR tiene seis secciones. Ni más ni menos. Lo que lo hace útil es que cabe en una página. Si necesitas más de una página, seguro que estás mezclando decisiones.

# ADR-007: Usar arquitectura event-driven para notificaciones

## Estado
Aceptado (2026-03-15)

## Contexto
El sistema de notificaciones actual usa polling desde el frontend cada 30
segundos. Con 2.000 usuarios concurrentes esto genera ~4.000 requests/minuto
al endpoint de notificaciones. El 95% de esas requests devuelven un array
vacío porque no hay notificaciones nuevas.

El equipo de infraestructura ha reportado que el endpoint de polling es el
segundo mayor consumidor de recursos del backend, por detrás del dashboard
principal. Las métricas de New Relic muestran un p99 de 800ms en horas punta
porque las queries de polling compiten con las queries de negocio.

## Decisión
Implementar un sistema de notificaciones basado en eventos usando
Mercure como hub SSE (Server-Sent Events). El backend publica eventos
al hub de Mercure cuando se genera una notificación. El frontend mantiene
una conexión SSE abierta y recibe las notificaciones en tiempo real.

Elegimos Mercure sobre WebSockets porque:
- Es un protocolo estándar (RFC) diseñado para este caso de uso exacto
- Se integra con Symfony de forma nativa a través del componente Mercure
- Soporta reconexión automática y reconciliación de eventos perdidos
- No requiere mantener estado de conexión en el backend

## Alternativas consideradas

| Alternativa | Pros | Contras |
|---|---|---|
| **Polling optimizado** (60s interval) | Sin cambios en arquitectura, simple | Sigue desperdiciando recursos, latencia de hasta 60s |
| **WebSockets** (Ratchet/Swoole) | Bidireccional, baja latencia | Requiere servidor persistente, gestión de estado de conexiones, más complejo de escalar |
| **Mercure SSE** | Unidireccional (suficiente para notificaciones), protocolo estándar, auto-reconnect, integración nativa con Symfony | Componente bastante nuevo, requiere hub como servicio adicional |
| **Firebase Cloud Messaging** | Push nativo en móvil, gestionado | Dependencia externa, no funciona para web sin service worker, coste a escala |

## Consecuencias

### Positivas
- Eliminamos ~3.800 requests/minuto de polling vacío
- Latencia de notificaciones pasa de 30s (media) a <1s
- El hub de Mercure se despliega como contenedor Docker independiente,
  con escalado horizontal

### Negativas
- Añadimos un servicio más a la infraestructura (hub Mercure)
- El equipo necesita familiarizarse con SSE y el protocolo Mercure
- Necesitamos un fallback para navegadores que no soporten SSE (< 1%
  según nuestro analytics, aceptamos el riesgo)

### Neutras
- Las notificaciones existentes en base de datos se mantienen como están.
  El endpoint de "obtener notificaciones" sigue existiendo para la carga
  inicial. Solo eliminamos el polling.

Eso es todo. Una página. Contexto, decisión, alternativas, consecuencias. Cualquiera que lea esto dentro de un año sabe por qué elegimos Mercure, qué otras opciones había, y qué trade-offs asumimos.

Reglas para escribir ADRs que tu equipo leerá

Llevo usando ADRs en tres proyectos distintos. Estas son las reglas que he ido puliendo:

Guárdalos en el repositorio. No en Confluence, no en Notion, no en Google Drive. En el repositorio, junto al código. doc/adr/ o doc/decisions/. Así viajan con el código, se revisan en los pull requests, y cualquier agente de IA que lea el repo los encuentra.

Numeración secuencial con nombre descriptivo. 001-usar-redis-para-sesiones.md, 002-adoptar-ddd-para-modulo-facturacion.md. La numeración da orden cronológico. El nombre descriptivo permite encontrarlos sin abrir el fichero.

Markdown puro. Sin herramientas especiales, sin formatos propietarios. Markdown se lee en cualquier editor, en GitHub, en un terminal, y los agentes de IA lo procesan sin problemas.

Nunca modifiques un ADR aceptado. Nunca. Si la decisión cambia, creas un nuevo ADR con estado “Supersede ADR-007”. El ADR original se marca como “Superseded by ADR-015”. El historial se preserva intacto. Esto es fundamental: si alguien necesita entender por qué se cambió de enfoque, puede leer ambos ADRs y entender la evolución.

Mantenlos cortos. Una página. Si te pasas, estás mezclando decisiones o entrando en detalles de implementación que no corresponden. El ADR captura el qué y el por qué de la decisión. El cómo va en el código y en las specs.

El proceso de escribirlo es tan valioso como el documento. Esto es algo que no esperaba cuando empecé. El acto de escribir un ADR te obliga a pensar con rigor. Te obliga a enumerar alternativas que quizá no habías considerado. Te obliga a articular las consecuencias. He descartado decisiones que parecían obvias después de escribir el ADR y darme cuenta de que las consecuencias no me convencían.

La plantilla mínima

Si quieres empezar hoy, copia esto en doc/adr/000-template.md:

# ADR-NNN: [Título descriptivo de la decisión]

## Estado
[Propuesto | Aceptado | Deprecado | Reemplazado por ADR-XXX]

## Contexto
[Qué problema tenemos. Qué restricciones hay. Qué fuerzas entran en
juego. Datos concretos si los hay (métricas, volúmenes, plazos).]

## Decisión
[Qué hemos decidido hacer y por qué esta opción sobre las demás.]

## Alternativas consideradas

| Alternativa | Pros | Contras |
|---|---|---|
| Opción A | ... | ... |
| Opción B | ... | ... |

## Consecuencias
[Qué cambia a partir de esta decisión. Tanto lo positivo como lo
negativo. Qué trade-offs asumimos a sabiendas.]

Seis secciones como base. Si necesitas más, añade un campo de “Participantes” o “Fecha de revisión”. Pero empieza simple — ya complicarás cuando lo necesites.

ADRs y agentes de IA: donde la cosa se pone interesante

Aquí es donde los ADRs pasan de “buena práctica de documentación” a algo que no puedes no tener. Porque no eres solo tú quien lee las decisiones arquitectónicas — tus agentes de IA también las leen.

Cuando Claude Code explora un repositorio, lee el CLAUDE.md, lee la estructura del proyecto, y lee los ficheros de configuración. Pero si además tiene un directorio doc/adr/ con las decisiones arquitectónicas, entiende el por qué del proyecto. No solo sabe que usas Redis — sabe por qué usas Redis y qué alternativas se descartaron.

Esto tiene consecuencias prácticas:

El agente respeta las decisiones existentes. Si un ADR dice que elegiste event-driven para notificaciones, y le pides al agente que añada un nuevo tipo de notificación, el agente implementará eventos — no polling. Sin el ADR, el agente podría proponer cualquier enfoque.

Detectas contradicciones antes de que lleguen a código. Si un agente propone una solución que contradice un ADR, lo ves en la revisión. “Espera, el ADR-007 dice que usamos Mercure para notificaciones en tiempo real, ¿por qué estás proponiendo un cron job que hace polling?”

Los ADRs alimentan las specs de SDD. En mi flujo con SDD y OpenSpec, las especificaciones definen qué construir. Los ADRs definen las restricciones y el por qué. Un agente que lee ambos produce código mucho mejor — no es lo mismo “implementa notificaciones” que “implementa notificaciones respetando que usamos Mercure y event-driven”.

Un ejemplo concreto: la spec dice “implementar sistema de notificaciones para alertas de facturación vencida”. Sin ADRs, el agente podría implementar un cron que consulta facturas vencidas cada minuto. Con el ADR-007 en el repositorio, el agente sabe que el sistema de notificaciones es event-driven con Mercure, y emite un evento InvoiceOverdueNotification cuando se detecta una factura vencida. En mi caso, esa diferencia me ahorró dos rondas de revisión de PR — el agente acertó el enfoque a la primera porque tenía el contexto.

ADRs como contexto en Agent Teams

Si usas Agent Teams en Claude Code, los ADRs se convierten en el “cerebro compartido” del equipo de agentes. El Architect lee los ADRs para proponer diseños consistentes. El Code Reviewer los usa para validar que la implementación respeta las decisiones. El Devil’s Advocate los cita cuando cuestiona una propuesta.

En la práctica, he añadido una instrucción en las configuraciones de mis agentes:

Antes de proponer cualquier diseño, lee todos los ADRs en `doc/adr/`.
Tus propuestas DEBEN ser consistentes con las decisiones aceptadas.
Si necesitas contradecir un ADR existente, propón un nuevo ADR que lo
reemplace, con justificación detallada.

Esto cierra el ciclo. Las decisiones se documentan, los agentes las leen, los agentes las respetan, y cuando una decisión debe cambiar, el flujo exige documentar el cambio. Nada se pierde.

Cuándo NO escribir un ADR

No todas las decisiones merecen un ADR. Si aplicas ADRs a todo, acabarás con un directorio inmanejable que nadie lee. Estas son las decisiones que no necesitan ADR:

Decisiones triviales. Usar kebab-case en nombres de archivo. Usar PSR-12 como estándar de código. Estas son convenciones del proyecto que van en el CLAUDE.md o en el phpstan.neon, no en un ADR.

Decisiones que se revierten en una hora. El color del botón de “Enviar”. La librería de formateo de fechas. Si cambiarla cuesta una hora y no tiene consecuencias arquitectónicas, no necesita ADR.

Decisiones donde el código es autoexplicativo. Si usas un patrón Repository y todo el equipo lo entiende, no necesitas un ADR que diga “decidimos usar Repository”. El código lo dice.

La regla que uso: si dentro de 6 meses alguien podría preguntar “¿por qué hicimos esto?” y la respuesta no es obvia mirando el código, necesita un ADR. Si la respuesta es obvia, no.

Algunos ejemplos de decisiones que merecen ADR:

  • Elegir entre DDD y Transaction Script para un módulo
  • Migrar de monolito a microservicios (o decidir no hacerlo)
  • Elegir la estrategia de autenticación (JWT vs sesiones vs OAuth)
  • Decidir el patrón de comunicación entre servicios (sync vs async)
  • Elegir la base de datos (PostgreSQL vs MongoDB para un caso concreto)
  • Adoptar una nueva herramienta que afecta al flujo de trabajo del equipo

Herramientas para gestionar ADRs (y por qué no las necesitas)

Existen herramientas para gestionar ADRs:

  • adr-tools: Scripts de bash para crear y gestionar ADRs. adr new "Usar Redis para sesiones" y te genera el fichero con la plantilla y la numeración correcta.
  • log4brains: Herramienta Node.js que genera una web estática navegable a partir de tus ADRs. Útil si tienes muchos ADRs y quieres buscar entre ellos sin abrir ficheros uno a uno.

Pero siendo honesto: una carpeta con markdowns numerados funciona de sobra. Llevo meses así y no he necesitado más. doc/adr/001-titulo.md, doc/adr/002-titulo.md. Simple, sin dependencias, funciona con cualquier editor y cualquier agente.

Si tienes más de 50 ADRs y necesitas buscar entre ellos, quizá log4brains merezca la pena. Para los demás, un ls doc/adr/ es suficiente.

El coste real de los ADRs (y de no tenerlos)

No voy a venderte los ADRs como algo gratuito. Tienen un coste real:

Tiempo de escritura. Un ADR bien escrito lleva entre 15 y 30 minutos. Si tomas dos decisiones arquitectónicas por sprint, es una hora de sprint dedicada a documentar decisiones.

Fricción. Hay un momento de “¿de verdad necesito escribir esto?” cada vez que tomas una decisión. Esa fricción es molesta. Pero también es el mecanismo que te obliga a pensar dos veces.

Mantenimiento del directorio. Con el tiempo, algunos ADRs quedan superseded, otros deprecated. Necesitas revisarlos cada cierto tiempo para que el directorio no se convierta en un cementerio de decisiones irrelevantes.

Ahora el otro lado de la balanza. El coste de no tener ADRs:

He visto a un equipo reescribir un módulo de caché completo porque nadie recordaba por qué se eligió la estrategia de invalidación. Tres semanas de trabajo. El nuevo módulo acabó con la misma estrategia que el original, porque las razones seguían siendo válidas — solo que nadie las había documentado.

He visto a un CTO nuevo llegar a una empresa, decidir que “esto hay que modernizarlo”, migrar de un monolito a microservicios, y descubrir seis meses después que el monolito era deliberado porque el equipo era de tres personas y no podían mantener la complejidad operativa de microservicios. Si hubiera habido un ADR-003 que dijera “mantenemos monolito porque el equipo es de 3 personas y el coste operativo de microservicios no compensa”, quizá se habría ahorrado medio año.

En el lado positivo: en un proyecto donde sí teníamos ADRs, un desarrollador nuevo leyó el ADR-005 (“usamos monolito modular, no microservicios”) y entendió el contexto en 5 minutos. No hizo la pregunta de “¿por qué no microservicios?” que en otros equipos me ha costado reuniones de una hora.

30 minutos de escritura vs semanas de retrabajo. La cuenta sale.

Cómo empezar con ADRs mañana mismo

No necesitas un proceso formal ni la aprobación de nadie. Mañana, cuando tomes una decisión técnica que no sea trivial, abre un fichero markdown y escribe el contexto, la decisión, las alternativas y las consecuencias. Guárdalo en doc/adr/001-titulo.md. Haz commit.

Si trabajas con agentes de IA, añade una línea en tu CLAUDE.md:

Las decisiones arquitectónicas están documentadas en `doc/adr/`.
Léelas antes de proponer cambios que afecten a la arquitectura.

Eso es todo. Un directorio, una plantilla, y el hábito de escribir 15 minutos cada vez que tomas una decisión que importa. Dentro de seis meses, cuando alguien pregunte “¿por qué usamos Mercure en vez de WebSockets?”, tendrás la respuesta. Y no será “creo que era porque…” sino un documento con contexto, alternativas evaluadas y consecuencias asumidas.

Tu yo de dentro de seis meses te dará las gracias. Y tus agentes, también.


P.D.: Si ya usas ADRs o tienes tu propio sistema para documentar decisiones, me interesa saber cómo lo haces. Estoy en Twitter como @lmmartinb.

Luis Miguel Martín
Luis Miguel Martín

CTO en LCApps. Escribo sobre Claude Code, MCP, agentes IA y arquitectura de software real.

💡

¿Te ha gustado este artículo?

Descubre patrones de diseño, decisiones técnicas y arquitecturas escalables.