Una feature, fino in fondo
Il caso di partenza elencava tre aree candidate. Ne ho costruita una sola, portandola end-to-end: tracking live dei mezzi su mappa nativa, con due punti d’ingresso (il tap sul marker e la riga dell’elenco) che portano alla stessa pagina di dettaglio, alimentata dalla stessa sorgente in memoria.
Le altre due aree non sono state abbozzate come schermate vuote. Una schermata vuota è una promessa che il codice non mantiene: fa sembrare il prodotto più completo e lo rende meno credibile appena qualcuno la tocca.
Perché un dimostratore, e non un preventivo
Davanti a una richiesta così, la strada normale è stimare e discutere. Ma stimare una cosa che nessuno ha visto significa discutere di ipotesi, e le ipotesi si difendono male.
Un dimostratore che funziona sposta la conversazione: non si parla più di quanto potrebbe costare, si guarda cosa succede toccando un mezzo sulla mappa e si decide se serve.
C’è però una seconda ragione, e tacerla renderebbe questo pezzo meno utile: un dimostratore serve anche a convincere. La stessa richiesta si poteva soddisfare con una web application ordinaria, aperta dal browser del telefono, riusando gran parte di quello che c’era già. L’app nativa non era l’unica strada tecnica: era quella che si vede.
Vale la pena saperlo distinguere, quando si prende una decisione del genere. Non perché sia sbagliato scegliere anche per l’effetto (a volte far vedere è il lavoro) ma perché è una ragione diversa dalle altre, e va messa sul tavolo insieme alle altre invece che nascosta dietro di esse.
Le decisioni prima del codice
Ogni bivio è stato deciso prima di scrivere il codice corrispondente, e il motivo dello scarto è registrato insieme alla scelta. È una disciplina che costa poco durante e vale molto dopo: sei mesi più tardi, una scelta senza la sua alternativa non si distingue da un’abitudine, e nessuno sa più se fosse una decisione o un riflesso.
Il criterio è sempre lo stesso (cosa dimostra davvero questa scelta) ed è la versione da dimostratore della domanda che si fa su un progetto vero: dove conviene spendere il budget di rischio, che è uno solo e non si rigenera.
Il codice tiene la promessa dell’architettura
Un diagramma è una promessa; quello che la mantiene sono sei o sette punti precisi del codice. Il client HTTP è tipizzato e registrato in un composition root unico, non costruito a mano dentro le pagine. Il polling gira su un timer asincrono e non su un loop con attesa, cattura le eccezioni per tipo invece che con una rete generica, e marshalla l’aggiornamento della collezione sul thread dell’interfaccia, perché toccare da un thread di background una collezione osservata è il crash classico che si manifesta solo sul dispositivo di qualcun altro.
La parte che può davvero rompersi (la sincronizzazione della collezione condivisa) vive fuori dal framework, in una funzione pura e deterministica. È quella con più test, perché se sbaglia sbaglia per tutte e tre le pagine insieme. Svuotare e ricreare la lista a ogni aggiornamento sarebbe stato più semplice da scrivere, e avrebbe fatto sfarfallare mappa ed elenco ogni quattro secondi sotto gli occhi di chi guarda.
Un leak sulle sottoscrizioni al ciclo di vita non era stato previsto in fase di design: è emerso in code review, ed è stato corretto e coperto da test nello stesso giro. Sta scritto perché è più utile di una lista di cose andate bene.
La verifica, dichiarata per intero
I test automatici coprono il contratto del gateway e le trasformazioni pure e condivise della app. Le build e alcune prove runtime restano manuali. La guardia di avvio è testata con cento chiamate concorrenti; il wire format è verificato leggendo il JSON grezzo, non solo l’oggetto deserializzato, così una regressione del formato si vede prima che se ne accorga la app.
Quello che non è verificato è scritto insieme al motivo. Una verifica proporzionata dichiarata per intero dice più di una copertura alta di cui non si conosce il perimetro.
| Test automatici | Provato a mano | Non verificato | |
|---|---|---|---|
| Il contratto del gateway | |||
| Le trasformazioni pure e condivise | |||
| La guardia di avvio cento chiamate concorrenti | |||
| Il formato sul filo letto come JSON grezzo, non solo deserializzato | |||
| Le build | |||
| Alcune prove a runtime | |||
| L'esecuzione su Android verificata la compilazione, non l'esecuzione |
L'ultima riga è il motivo per cui questa tabella esiste. Una copertura alta di cui non si conosce il perimetro dice meno di una verifica proporzionata dichiarata per intero.