
Dal 2018 ricopro il ruolo di lead/capo/sviluppatore senior nel team — chiamatelo come preferite, ma la sostanza è che sono completamente responsabile di uno dei moduli e di tutti gli sviluppatori che ci lavorano. Questa posizione mi offre una nuova prospettiva sul processo di sviluppo, poiché sono coinvolto in un numero maggiore di progetti e partecipo attivamente al processo decisionale. Recentemente, grazie a queste due circostanze, ho realizzato quanto profondamente la comprensione influenzi il codice e l'applicazione.
L'idea che voglio esprimere è che la qualità del codice (e del prodotto finale) è strettamente legata a quanto consapevoli siano le persone che progettano e scrivono il codice riguardo a ciò che stanno facendo.
Probabilmente stai pensando: «Grazie, capo. Certamente, sarebbe bene capire ciò che scrivi. Altrimenti, potresti benissimo assumere un gruppo di scimmie per battere a caso sui tasti e andare avanti». E hai completamente ragione. Pertanto, accetto come dato di fatto che sei consapevole che avere una comprensione generale di ciò che fai è necessario. Questo può essere definito come un livello zero di comprensione, e non lo analizzeremo in dettaglio. Esamineremo in dettaglio ciò che è necessario capire e come questo influisce sulle decisioni che prendi ogni giorno. Se avessi conosciuto queste informazioni in anticipo, mi sarebbero state risparmiate molte ore sprecate e codice discutibile.
Anche se qui sotto non vedrai una riga di codice, ritengo che tutto ciò che è detto qui abbia una grande importanza per scrivere codice di qualità e significativo.
Primo livello di comprensione: Perché non funziona?
A questo livello, gli sviluppatori di solito ci arrivano nelle prime fasi della loro carriera, a volte anche senza alcun aiuto da parte degli altri — almeno secondo le mie osservazioni. Immagina di aver ricevuto un report di bug: una certa funzione nell'app non funziona e deve essere corretta. Come procederesti?
Lo schema standard è il seguente:
- Trovare il frammento di codice che causa il problema (come si fa questo è un argomento a parte, che affronto nel mio libro sul codice obsoleto)
- Apportare modifiche a quel frammento
- Assicurarsi che il bug sia stato corretto e che non siano emersi errori regressivi
Ora concentriamoci sul secondo punto — apportare modifiche al codice. Ci sono due approcci per questo processo. Il primo: capire esattamente cosa sta succedendo nel codice attuale, identificare l'errore e correggerlo. Il secondo: procedere a tentoni — aggiungere, ad esempio, +1 in un'istruzione condizionale o in un ciclo, vedere se la funzione ha iniziato a funzionare nel caso previsto, poi provare qualcos'altro e così via all'infinito.
Il primo approccio è quello corretto. Come spiega Steve McConnell nel suo libro Code Complete (che consiglio vivamente), ogni volta che apportiamo modifiche al codice, dobbiamo essere in grado di prevedere con certezza come questo influenzerà l'applicazione. Cito a memoria, ma se una correzione di bug non funziona come previsto, dovresti preoccuparti; devi mettere in discussione l'intero tuo piano d'azione.
In sintesi, per eseguire una correzione di bug efficace che non comprometta la qualità del codice, è necessario comprendere sia la struttura del codice sia la fonte del problema specifico.
Il secondo livello di comprensione: Perché funziona?
Questo livello è molto meno intuitivo rispetto al precedente. Io, quando ero ancora un programmatori alle prime armi, l'ho assimilato grazie al mio capo e in seguito l'ho spiegato più volte a nuovi arrivati.
Immagina di ricevere due report di bug contemporaneamente: il primo riguarda lo scenario A, il secondo lo scenario B. In entrambi gli scenari qualcosa non va. Pertanto, inizi prima con il primo bug. Seguendo i principi che abbiamo dedotto per il primo livello di comprensione, approfondisci il codice concernente il problema, scopri perché causa il comportamento dell'applicazione nello scenario A e apporti le modifiche ragionevoli che producono esattamente il risultato atteso. Tutto procede bene.
Poi passi allo scenario B. Ripeti lo scenario nel tentativo di provocare l'errore, ma — sorpresa! — ora tutto funziona come dovrebbe. Per confermare la tua ipotesi, annulli le modifiche apportate durante la risoluzione del bug A e il bug B riemerge. La tua correzione ha risolto entrambi i problemi. Che fortuna!
Non te lo aspettavi affatto. Hai trovato un modo per correggere l'errore nello scenario A e non hai idea del perché funzioni anche nello scenario B. A questo punto, è molto allettante concludere che entrambi i compiti siano stati completati con successo. È piuttosto logico: l'obiettivo era eliminare gli errori, vero? Ma il lavoro non è ancora finito: devi ancora capire perché le tue azioni hanno corretto l'errore nello scenario B. Perché? Perché potrebbe funzionare secondo principi errati, e in tal caso dovrai cercare un'altra soluzione. Ecco un paio di esempi di tali situazioni:
- Poiché la soluzione non è stata progettata specificamente per l'errore B tenendo conto di tutti i fattori, potresti aver distrutto, senza saperlo, la funzione C.
- Non è escluso che ci sia anche un terzo bug, collegato alla stessa funzione, e che la tua correzione legi il corretto funzionamento del sistema nello scenario B su di esso. Adesso tutto sembra a posto, ma un bel giorno quel terzo bug verrà notato e corretto. Allora nello scenario B si presenterà di nuovo un errore, e speriamo solo che sia lì.
Tutto ciò introduce caos nel codice e prima o poi ti ricadrà addosso — probabilmente nel momento meno opportuno. Dovrai avere la forza di volontà per dedicare tempo a capire perché tutto sembra funzionare, ma ne vale la pena.
Terzo livello di comprensione: Perché funziona?
La mia recente illuminazione riguarda proprio questo livello,e probabilmente sarebbe stato il più vantaggioso se ci fossi arrivato prima.
Per essere più chiari, analizziamo un esempio: il tuo modulo deve essere compatibile con la funzione X. Non conosci molto bene la funzione X, ma ti è stato detto che per la compatibilità occorre utilizzare il framework F. Altri moduli che si integrano con X funzionano proprio con esso.
Il tuo codice non ha mai interagito con il framework F sin dal primo giorno, quindi implementarlo non sarà affatto semplice. Ciò avrà conseguenze significative su alcuni aspetti del modulo. Tuttavia, ti immergi completamente nello sviluppo: scrivi codice per settimane, testi, rilasci versioni pilota, ricevi feedback, correggi errori di regressione, scopri complicazioni impreviste, non rispetti le scadenze iniziali, scrivi ancora un po' di codice, testi, ricevi riscontri, correggi errori di regressione: tutto questo per implementare il framework F.
E a un certo punto realizzi improvvisamente — o magari lo senti da qualcuno — che, forse, il framework F non ti darà affatto compatibilità con la funzione X. Forse, tutto questo tempo e tutte queste energie sono stati investiti nel modo sbagliato.
Qualcosa di simile è accaduto una volta durante il lavoro su un progetto di cui ero responsabile. Perché è successo? Perché non capivo bene la funzionalità X e come essa si collegasse al framework F. Cosa avrei dovuto fare? Chiedere a chi avanza la richiesta di sviluppo di spiegarmi chiaramente come il piano d'azione previsto portasse al risultato desiderato, anziché semplicemente ripetere ciò che era stato fatto per altri moduli o credere sulla parola che fosse necessario per il funzionamento della funzionalità X.
L'esperienza di questo progetto mi ha insegnato a rinunciare a iniziare il processo di sviluppo fino a quando non abbiamo chiaro perché ci viene chiesto di eseguire determinate azioni. Rinunciare esplicitamente. Quando ricevi un compito, il primo impulso è di occuparsene immediatamente per non perdere tempo. Ma la politica di «congelare il progetto fino a quando non comprendiamo tutti i dettagli» può ridurre il tempo sprecato in modo significativo.
Anche se qualcuno cerca di farti pressione per iniziare a lavorare, anche se non capisci il motivo — resisti. Prima di tutto, chiarisci quale obiettivo c'è dietro questa richiesta e valuta se sia il giusto percorso per raggiungerlo. Ho dovuto apprendere tutto ciò attraverso esperienze difficili — spero che il mio esempio possa semplificare la vita a chi legge.
Quarto livello di comprensione: ???
Nella programmazione c'è sempre qualcosa di nuovo da apprendere, e credo di aver toccato solo la superficie della comprensione. Quali altri livelli di comprensione hai scoperto negli anni di lavoro con il codice? Quali decisioni hai preso che hanno avuto un impatto positivo sulla qualità del codice e dell'applicazione? Quali decisioni si sono rivelate errate e ti hanno insegnato lezioni preziose? Condividi le tue esperienze nei commenti.
Fonte: habr.com
