Esiste un modo di sviluppare software che sembra progresso e non lo è. Una funzionalità viene rilasciata ogni pochi giorni, la demo migliora ogni settimana e per un po’ il grafico non fa che salire. Poi arriva la terza integrazione, o il primo vero cliente con dati reali, e il team scopre che la forma del sistema non può accoglierli. Il lavoro dei sei mesi successivi consiste nel disfare quello degli ultimi tre.
Abbiamo scelto il compromesso opposto. I nostri progetti iniziano lentamente, di proposito.
La velocità è un prestito su una decisione non ancora presa
Gran parte della velocità iniziale deriva dal rinviare le decisioni anziché prenderle. Non si sceglie un modello di dati: si accetta quello implicito nella prima schermata. Non si definisce un confine tra due aspetti: li si lascia condividere un file promettendo di separarli più avanti. Ogni rinvio è un piccolo prestito, e gli interessi si pagano a ogni modifica successiva.
I prestiti che fanno male non sono quelli di cui si parla di solito. Il markup duplicato si corregge con poco. Un nome di variabile poco chiaro si corregge con poco. Ciò che non costa poco:
- Il modello di dati. Ogni schermata, esportazione, controllo dei permessi e integrazione finisce per dipendere dalla forma delle entità principali. Sbagliarlo non produce un bug, produce una categoria di bug che si ripresenta per anni.
- I confini tra i sistemi. Una volta che due componenti conoscono ciascuno la struttura interna dell’altro, separarli richiede di intervenire su entrambi. Il costo di questa separazione cresce a ogni funzionalità costruita a cavallo della giunzione.
- Il significato di una parola. Se «account» significa una cosa nella fatturazione e un’altra nella reportistica, l’ambiguità verrà codificata in cento funzioni prima che qualcuno dia un nome al problema.
Nessuno di questi problemi si risolve con uno sprint di refactoring. Si risolvono con una riscrittura, che è il modo più costoso di imparare qualcosa che si sarebbe potuto capire riflettendo per una settimana.
In che cosa consiste davvero l’avvio lento
Costruire lentamente non equivale a costruire con cura in senso generico. È un insieme preciso di attività, e si svolgono prima che ci sia molto da vedere.
Dedichiamo la prima fase a cercare di formulare il problema in una frase. Non la soluzione: il problema. È più difficile di quanto sembri, ed è lì che la maggior parte dei prodotti fallisce senza far rumore. Un problema che non si riesce a formulare con precisione è un problema che si risolverà in modo approssimativo, e una soluzione approssimativa a un problema poco chiaro non si distingue da un prodotto di cui nessuno ha bisogno.
Poi il modello. Descriviamo le entità, le loro relazioni e le loro invarianti prima di progettare una schermata, perché le schermate discendono dal modello e non il contrario. Se il modello è giusto, le interfacce sono poco costose da cambiare e possono continuare a migliorare per anni. Se il modello è sbagliato, nessun lavoro sull’interfaccia potrà nasconderlo.
Poi i confini. Decidiamo che cosa ciascuna parte del sistema può conoscere, e lo mettiamo per iscritto. I confini sono l’unica difesa contro l’esito in cui una codebase diventa tecnicamente funzionante e in pratica non più modificabile.
Solo allora inizia l’implementazione, e l’implementazione è relativamente rapida, perché le domande costose hanno già avuto risposta.
Il problema della misurazione
La difficoltà, detta onestamente, è che questo approccio è difficile da dimostrare mentre è in corso. Un team che dedica tre settimane a un modello di dati non ha nulla da mostrare alla fine, se non un documento e alcune definizioni di tipi. Un team che dedica tre settimane a costruire schermate ha delle schermate.
La differenza emerge più tardi, e si manifesta come un’assenza. Non c’è una riscrittura. Non c’è un mese passato a migrare dati perché una colonna significava due cose. Non c’è una funzionalità che si rivela impossibile. Le assenze sono difficili da inserire in un rapporto sullo stato di avanzamento, ed è proprio per questo che gli incentivi nella maggior parte delle aziende vanno nella direzione opposta.
Accettiamo questo costo perché siamo proprietari di ciò che costruiamo. Nessuno ci chiede di mostrare un grafico questo trimestre, quindi possiamo ottimizzare lo stato del sistema al quinto anno anziché lo stato della demo alla terza settimana.
Dove serve la velocità
Questo non è un elogio della lentezza come virtù. La lentezza nel posto sbagliato è solo spreco.
Una volta stabiliti il modello e i confini, vogliamo muoverci molto in fretta, e lo scopo dell’avvio lento è proprio poterlo fare. I sistemi ben definiti sono piacevoli da estendere. Aggiungere una funzionalità è una modifica locale anziché una trattativa con l’intera codebase. La velocità arriva più tardi, ma arriva e poi resta, invece di diminuire man mano che il sistema accumula decisioni che nessuno ha preso deliberatamente.
Il software che dura un decennio non è software costruito con più cura in senso generico. È software in cui le poche decisioni davvero irreversibili sono state prese lentamente, e tutto il resto rapidamente.
