Template ADR ed esempi: formati pronti da copiare

La riunione andava avanti da due ore. Stavamo discutendo se usare PostgreSQL o MongoDB per un nuovo servizio. Gli argomenti rimbalzavano da una parte all'altra: integrità relazionale, schemi flessibili, familiarità del team, complessità operativa.

Poi qualcuno ha detto: "Ma non avevamo fatto esattamente questa discussione l'anno scorso per lo user service?"

Silenzio. Era così. Un anno prima avevamo passato lo stesso tempo sulla stessa domanda e avevamo scelto PostgreSQL. Nessuno ricordava il ragionamento, e l'ingegnere che aveva guidato quella discussione aveva lasciato l'azienda. Così stavamo rifacendo lo stesso dibattito da zero.

È lì che abbiamo iniziato a scrivere Architecture Decision Record. Questo post è la parte da copiare e incollare: tre template, quando usare ciascuno, e tre delle nostre decisioni già compilate.

Cos'è un ADR?

Un architecture decision record è un documento breve che cattura una singola decisione architetturale. Non un documento di design che copre tutto di un sistema, ma una sola decisione:

  • Cosa abbiamo deciso
  • Perché l'abbiamo deciso
  • Quali alternative abbiamo considerato
  • Quali sono le conseguenze

Un ADR dovrebbe stare in una o due pagine. Se è più lungo, probabilmente stai documentando più di una decisione. La guida completa spiega quando scriverne uno, il ciclo di vita e gli errori che fanno i team; questo post si limita ai formati.

Il template minimo (Nygard)

Michael Nygard ha proposto il formato che la maggior parte dei team usa ancora in un post del 2011. Cinque parti: un titolo che è un breve sintagma nominale, uno stato, il contesto ("le forze in gioco"), la decisione ("la nostra risposta a queste forze") e le conseguenze ("il contesto risultante, dopo aver applicato la decisione"). I suoi stati sono Proposed, Accepted, Deprecated e Superseded.

# ADR-NNNN: Breve sintagma nominale

## Status

Proposed | Accepted | Deprecated | Superseded by ADR-XXXX

## Context

Quali forze sono in gioco? Tecniche, organizzative, scadenze, budget.
Sii specifico: numeri, vincoli, chi è coinvolto.

## Decision

Useremo ...

## Consequences

Cosa diventa più facile, cosa diventa più difficile, cosa dobbiamo tenere d'occhio.

Noi aggiungiamo una sezione: Alternatives Considered, tra Decision e Consequences. È la parte che fa risparmiare più tempo in seguito, perché la domanda che arriva un anno dopo raramente è "cosa abbiamo scelto?" e quasi sempre "perché non abbiamo scelto X?"

## Alternatives Considered

### Opzione B
- Pro: ...
- Contro: ...
- Perché scartata: ...

MADR

MADR (Markdown Architectural Decision Records) è un template più strutturato, mantenuto su adr.github.io/madr. La versione attuale è la 4.0.0, rilasciata il 17 settembre 2024. Mette i metadati nel frontmatter YAML e rende le opzioni e i loro compromessi sezioni esplicite anziché prosa.

---
status: proposed | rejected | accepted | deprecated | superseded by ADR-XXXX
date: AAAA-MM-GG
decision-makers: ...
consulted: ...
informed: ...
---

# Titolo breve del problema risolto e della soluzione scelta

## Context and Problem Statement

Due o tre frasi, oppure una breve storia. Idealmente formulato come una domanda.

## Decision Drivers

* Driver 1
* Driver 2

## Considered Options

* Opzione A
* Opzione B
* Opzione C

## Decision Outcome

Opzione scelta: "Opzione A", perché ...

### Consequences

* Positivo, perché ...
* Negativo, perché ...

### Confirmation

Come verificheremo che la decisione sia stata implementata (review, test, fitness function).

## Pros and Cons of the Options

### Opzione A

* Positivo, perché ...
* Negativo, perché ...

## More Information

Link, decisioni correlate, quando rivederla.

In MADR ogni sezione dopo Considered Options e Decision Outcome è facoltativa. Cancella quello che non usi invece di lasciare titoli vuoti.

Y-statement

Lo Y-statement fa stare una decisione in una sola frase. Viene da Uwe Zdun, Olaf Zimmermann e colleghi ("Sustainable Architectural Design Decisions", IEEE Software, 2013):

Nel contesto di <caso d'uso o componente>,
di fronte a <preoccupazione o requisito>,
abbiamo deciso per <opzione>
(e contro <altre opzioni>),
per ottenere <qualità o beneficio>,
accettando che <svantaggio>.

È troppo breve per contenere una sezione di contesto completa, ed è proprio questo il punto. Usalo per le decisioni che non meritano una pagina, come riga di sintesi in cima a un ADR più lungo, o come formato delle voci in un registro delle decisioni.

Quale formato usare

Formato Usalo quando Attenzione a
Nygard (più alternative) Stai iniziando, o la maggior parte delle decisioni ha un vincitore evidente e un paio di opzioni scartate Sezioni di contesto che diventano saggi
MADR Le decisioni sono contese, più persone devono approvarle, o vuoi che opzioni e driver siano confrontabili tra ADR Compilare ogni sezione facoltativa per abitudine
Y-statement Piccole decisioni, un registro delle decisioni, o una sintesi di una riga in cima a un ADR più lungo Usarlo per decisioni che hanno bisogno di una vera sezione di contesto

Scegline uno per il repository e mantienilo. Formati misti sono più difficili da scorrere di quanto qualsiasi formato singolo sia difficile da scrivere.

Esempio 1: ADR-0042, PostgreSQL per l'Order Service

È la decisione della storia iniziale, nel formato Nygard con la nostra sezione Alternatives Considered.

# ADR-0042: Usare PostgreSQL per l'Order Service

## Status

Accepted

## Context

L'order service ha bisogno di uno storage persistente per i dati degli ordini.
Prevediamo di elaborare inizialmente 50.000 ordini al giorno, fino a 500.000
entro due anni. Gli ordini hanno una struttura ben definita ma potrebbero
richiedere campi di metadati aggiuntivi nel tempo. Il team ha esperienza sia
con database relazionali sia con database documentali.

## Decision

Useremo PostgreSQL 15 come database principale dell'order service.

Abbiamo scelto PostgreSQL perché:

- La conformità ACID è necessaria per i dati finanziari degli ordini
- Le colonne JSON offrono flessibilità di schema per i metadati
- Il nostro team infrastrutturale ha esperienza operativa con PostgreSQL
- I pattern di query si prestano bene alla modellazione relazionale

## Alternatives Considered

### MongoDB

- Pro: storage JSON nativo, scalabilità orizzontale più semplice, evoluzione flessibile dello schema
- Contro: garanzie di consistenza più deboli, team meno esperto nella gestione operativa,
  richiederebbe strumenti aggiuntivi per le transazioni

### DynamoDB

- Pro: completamente gestito, operatività minima, scalabilità eccellente
- Contro: lock-in su AWS, pattern di query limitati all'accesso per partition/sort
  key, costi imprevedibili su larga scala

## Consequences

### Positive

- Forti garanzie di integrità dei dati
- Il team può riusare l'esperienza esistente su PostgreSQL
- Caratteristiche operative ben note

### Negative

- Le migrazioni di schema richiedono più pianificazione rispetto ai document store
- Scalabilità orizzontale più complessa se superiamo la capacità di un singolo nodo
- Serve sharding a livello applicativo se raggiungiamo i limiti di scala

### Risks

- Potrebbe essere necessario rivederla se il volume supera 1M di ordini al giorno
- Le query sulle colonne JSON sono meno efficienti rispetto ai document store nativi

## Related Decisions

- Vedi ADR-0007 per il motivo per cui accettiamo la consistenza eventuale
- Sostituisce ADR-0003, che raccomandava MongoDB

Sii specifico nel contesto. "Ci serve la conformità ACID" aiuta il prossimo lettore molto più di "ci serve affidabilità". E sii onesto nelle conseguenze: nessuna decisione è gratuita, e mettere per iscritto gli svantaggi dice ai team futuri quando è il momento di rivederla.

Esempio 2: ADR-0007, comunicazione event-driven, in MADR

La decisione su Kafka aveva più opzioni sul tavolo e più persone coinvolte, ed è lì che driver e opzioni espliciti di MADR si guadagnano il loro spazio.

---
status: accepted
---

# Usare Kafka per la comunicazione asincrona tra servizi

## Context and Problem Statement

I nostri microservizi comunicano in modo sincrono via HTTP. Questo li accoppia
strettamente, e un servizio non disponibile si trasforma in guasti a cascata a monte.
Come dovrebbero comunicare i servizi quando il chiamante non ha bisogno
di una risposta immediata?

## Decision Drivers

* Resilienza: un servizio fuori uso non deve trascinare con sé gli altri
* Accoppiamento più debole tra i servizi

## Considered Options

* Mantenere HTTP sincrono
* Comunicazione event-driven con Apache Kafka

## Decision Outcome

Opzione scelta: "Comunicazione event-driven con Apache Kafka", perché
elimina l'accoppiamento a runtime che causa i guasti a cascata.

### Consequences

* Positivo, perché i servizi diventano più resilienti ai guasti reciproci
* Negativo, perché passiamo dalla consistenza forte a quella eventuale
* Negativo, perché ora gestiamo un cluster Kafka
* Negativo, perché il team ha bisogno di formazione sui pattern event-driven

Stato e titolo restano quelli originali; i campi date e decision-makers qui mancano perché il record originale non li riportava.

Esempio 3: ADR-0015, un monorepo frontend, come Y-statement

La decisione sul monorepo sta in una frase, e la frase basta a rispondere a "perché è tutto in un solo repo?"

Nel contesto delle nostre 5 applicazioni frontend in repository separati,
di fronte a disallineamenti di versione e al costo di pubblicare pacchetti per condividere codice,
abbiamo deciso per un unico monorepo gestito con Nx
(e contro repository separati con una migliore gestione dei pacchetti, e contro un monorepo senza Nx),
per ottenere una condivisione del codice semplice e un'unica pull request che aggiorna il codice condiviso e tutti i suoi consumatori,
accettando che un repository più grande richiede la cache delle build e che le applicazioni possono accoppiarsi per errore.

Le opzioni scartate riportano le loro motivazioni nel record completo: i repository separati mantenevano il costo di coordinamento, e un monorepo senza Nx avrebbe reso proibitivi i tempi di build.

Dove conservarli

Metti gli ADR nel repository che descrivono, in docs/adr/ o docs/decisions/ (Nygard suggeriva doc/arch/adr-NNN.md). Sono versionati con il codice, compaiono nella code review e non possono perdersi in un wiki.

Numerali in sequenza e non riusare mai un numero. Quando una decisione cambia, scrivi un nuovo ADR che sostituisce il vecchio e aggiorna lo stato di quello vecchio, invece di modificare la storia.

Il passaggio che la maggior parte dei team salta è collegare un ADR alla parte del sistema che riguarda. In Archyl, un ADR ha i campi di Nygard (stato, contesto, decisione, conseguenze) più una data e un link "sostituito da", e si collega ai sistemi, ai container e ai componenti che ha plasmato. Chiunque guardi l'order service nel diagramma C4 vede ADR-0042 accanto: "Perché questo servizio usa PostgreSQL?" è a un clic di distanza.

Le trappole (scrivere gli ADR a cose fatte, lasciarli arrivare a cinque pagine, abbandonare la pratica dopo il primo trimestre impegnativo) sono trattate nella guida completa.

Inizia con uno

Quella riunione di due ore su PostgreSQL vs MongoDB? La volta successiva in cui è saltata fuori la domanda, si è chiusa in dieci minuti, perché qualcuno ha trovato ADR-0042. Vincoli, alternative e ragionamento erano tutti lì. Abbiamo confermato che la decisione originale valeva ancora e siamo andati avanti.

Scegli uno dei template qui sopra e incollalo in docs/adr/0001-record-architecture-decisions.md. Il tuo primo ADR può essere la decisione di scrivere ADR.


Approfondisci la documentazione dell'architettura: la guida al modello C4 | Perché la documentazione è importante | Documentare i flussi utente