Cosa devo fare come revisore quando il codice "deve essere" errato?


18

Lavoro a un progetto mal progettato e ingegnerizzato in cui sono state introdotte revisioni obbligatorie del codice alcuni mesi fa.

Mi è stato chiesto di rivedere una parte sostanziale del codice che implementa una nuova funzionalità. Presenta gli stessi difetti del resto della nostra base di codice. Capisco che in larga misura, quei difetti si insinuano nel nuovo codice, ad es. dovendo ereditare da una classe mal progettata o implementare un'interfaccia mal progettata mantenendo la compatibilità, e non posso davvero offrire una soluzione molto migliore che non implicherebbe la riscrittura della metà della base di codice. Ma ritengo che dal punto di vista ingegneristico, non serva a una base di codice già rotta che stiamo rompendo ancora di più. Il codice che sto recensendo è decisamente negativo, ma deve essere se la funzionalità deve essere implementata.

Come devo comportarmi riguardo a questa particolare recensione? C'è un modo per me di mantenere l'integrità e rimanere costruttivo?

Si noti che sto chiedendo informazioni sul limite della revisione del codice in questo contesto. Mi rendo conto che il problema è causato da altri fattori tra cui l'organizzazione e la cultura del lavoro, ma quello che voglio sapere è come gestire la recensione stessa.


1
Lo sviluppatore ha documentato il motivo per cui è stato fatto in questo modo e forse ha persino scritto un problema per il problema principale sul tracker dei problemi?
Luc Franken,

Poco e niente.
Red

4
C'è qualche appetito all'interno della tua organizzazione a fare qualcosa riguardo al debito tecnico?
Robert Harvey,

5
Se il codice non è sbagliato considerando l'ambiente, non attaccherei il codice. Per lo più da quando hai scritto non vedi un reale miglioramento possibile in questo momento. Creerebbe conflitti senza opportunità di miglioramento. Quello che vorrei fare è insegnare a documentare meglio come primo passo. In secondo luogo creare una procedura per farli scrivere problemi chiari. Ciò comporterà 2 cose: documentazione sul problema in modo che il prossimo possa funzionare più velocemente. Inoltre, viene visualizzato un elenco di problemi concreti da risolvere potenzialmente. Mi piace menzionare GitHub perché vedi quante volte si ripete se lo taggano.
Luc Franken,

Risposte:


25

Semplice:

1. Documenta il tuo debito tecnico

Hai identificato un pezzo di codice che funziona ma presenta alcuni problemi tecnici. Questo è debito tecnico . Come altri tipi di debito, nel tempo peggiora se non viene gestito.

Questo primo passo nella gestione del debito tecnico è documentarlo. Fallo aggiungendo elementi nel tracker dei problemi che descrivono il debito. Questo ti aiuterà a ottenere un quadro più chiaro dell'entità del problema e ti aiuterà anche a elaborare un piano per affrontarlo.

2. Rimborsare gradualmente il debito

Modifica il processo di sviluppo del software per tenere conto del rimborso del debito tecnico. Ciò potrebbe comportare lo sprint di indurimento occasionale o semplicemente la risoluzione di elementi di debito a intervalli regolari (ad esempio, 2 elementi a settimana). La parte importante per assicurarti di tagliare il tuo debito più velocemente del suo accumulo (il debito, anche il debito tecnico, ha interessi su di esso).

Una volta raggiunto un punto in cui non si ha più un deficit tecnico, si sta raggiungendo una base di codice più sana :)


5

Come nota a margine: cerca un nuovo lavoro. Questo non starebbe meglio.

Gli obiettivi del codice che stai esaminando sono:

  • Per spedire una funzione, che dovrebbe funzionare in base ai requisiti.

  • Ridurre la crescita del debito tecnico.

Il primo obiettivo viene esaminato verificando che l'unità, l'integrazione, il sistema e i test funzionali siano qui, che siano pertinenti e che coprano tutte le situazioni che devono essere testate. Devi anche verificare le convinzioni che l'autore originale potrebbe avere riguardo al linguaggio di programmazione, che potrebbe portare a bug sottili o al codice che finge di fare qualcosa di diverso da ciò che effettivamente fa.

Il secondo obiettivo è quello su cui la tua domanda è focalizzata. Da un lato, il nuovo codice non dovrebbe aumentare il debito tecnico. D'altro canto, l'ambito della revisione è il codice stesso, ma nel contesto dell'intera base di codice. Da lì, tu, come revisore, puoi aspettarti due approcci dall'autore originale:

  • Il codice esterno non è colpa mia. Ho appena implementato la funzione e non mi interessa l'intera base di codice.

    In questa prospettiva, il codice copierà i difetti della base di codice, e quindi inevitabilmente aumenterà il debito tecnico: più cattivo codice è sempre peggio.

    Sebbene questo sia un valido approccio a breve termine, a lungo termine comporterebbe ritardi crescenti e bassa produttività, e alla fine porterebbe a un processo di sviluppo così costoso e rischioso che il prodotto smetterebbe di evolversi.

  • Scrivere un nuovo codice è un'opportunità per riformattare quello precedente.

    In questa prospettiva, l'effetto dei difetti del codice legacy su quello nuovo potrebbe essere limitato. Inoltre, il debito tecnico potrebbe essere ridotto, o almeno non aumentato proporzionalmente alla crescita del codice.

    Sebbene si tratti di un valido approccio a lungo termine, presenta rischi a breve termine. Il principale è che, a breve termine, a volte ci vorrebbe più tempo per spedire la funzionalità specifica. Un altro aspetto importante è che se il codice legacy non è testato, il refactoring presenta un rischio enorme di introdurre regressioni.

A seconda della prospettiva che si desidera incoraggiare, si può essere propensi a consigliare ai revisori di rifattorizzare di più o meno. In ogni caso, non aspettarti un pezzo di codice impeccabile e pulito con una bella architettura e design all'interno di una base di codice scadente. Ciò che non dovresti incoraggiare è il comportamento in cui uno sviluppatore esperto che deve lavorare su una base di codice schifosa cerca di fare bene la sua parte . Invece di semplificare le cose, le rende solo più complicate di prima. Ora, invece di un cattivo codice uniforme, hai una parte con motivi di progettazione, un'altra parte con un codice pulito e chiaro, un'altra parte che è stata ampiamente riformulata nel tempo e nessuna unità di sorta.

Immagina, ad esempio, di scoprire una base di codice legacy di un sito Web di medie dimensioni. Sei sorpreso dalla mancanza di una normale struttura e dal fatto che la registrazione, una volta eseguita, viene eseguita aggiungendo manualmente le cose a un file di testo, invece di utilizzare un framework di registrazione. Decidi per la nuova funzionalità di utilizzare MVC e un framework di registrazione.

Il tuo collega sta implementando un'altra funzione ed è molto sorpreso dalla mancanza di un ORM in cui si farebbe una dimensione perfetta. Quindi inizia a usare un ORM.

Né tu, né il tuo collega siete in grado di esaminare centinaia di migliaia di righe di codice per utilizzare MVC, un framework di registrazione o un ORM ovunque. In realtà, richiederebbe mesi di lavoro: immagina di introdurre MVC; quanto tempo ci vorrebbe? O che dire di un ORM in situazioni in cui le query SQL sono state generate caoticamente attraverso la concatenazione (con posizioni occasionali per SQL Injection) all'interno del codice che nessuno poteva capire?

Pensi di aver fatto un ottimo lavoro, ma ora un nuovo sviluppatore che si unisce al progetto deve affrontare molta più complessità rispetto a prima:

  • Il vecchio modo di trattare le richieste,

  • The MVC way,

  • Il vecchio meccanismo di registrazione,

  • Il framework di registrazione,

  • L'accesso diretto al database con query SQL integrate al volo,

  • L'ORM.

In un progetto su cui stavo lavorando, c'erano quattro (!) Framework di registrazione usati fianco a fianco (più registrazione manuale). Il motivo è che ogni volta che qualcuno voleva registrare cose non c'era un approccio comune per farlo, quindi invece di imparare un nuovo framework (che in tutti i casi veniva usato solo nel 5% della base di codice), si aggiungeva semplicemente un altro lo sa già. Immagina il casino.

Un approccio migliore potrebbe essere il refactoring della base di codice un passo alla volta. Prendendo ancora una volta l'esempio della registrazione, il refactoring consisterebbe nei seguenti piccoli passi:

  • Trova tutti i luoghi in cui viene eseguita la registrazione legacy (ovvero quando si accede direttamente al file di registro) e assicurarsi che tutti chiamino gli stessi metodi.

  • Sposta questo codice in una libreria dedicata, se applicabile. Non desidero registrare la logica di archiviazione nella mia classe del carrello.

  • Modificare, se necessario, l'interfaccia dei metodi di registrazione. Ad esempio, possiamo aggiungere un livello che indica se il messaggio è informale o è un avviso o un errore.

  • Utilizzare i metodi appena refactored nella nuova funzionalità.

  • Migrare al framework di registrazione: l'unico codice interessato è il codice all'interno della libreria dedicata.


1
Ottima risposta fino all'ultimo paragrafo. Che tu lo voglia o no, stai insinuando che le buone pratiche non dovrebbero essere utilizzate per il nuovo codice. -1
RubberDuck,

2
@RubberDuck: non si tratta di buone pratiche, si tratta di scrivere codice radicalmente diverso dalla base di codice rimanente. Sono stato quello sviluppatore e ho visto le conseguenze di quello che ho fatto: avere un codice drasticamente migliore tra i codici cattivi peggiora solo le cose; ciò che lo rende migliore è migliorare la base di codice attraverso piccoli passaggi di refactoring. Aggiungerò un esempio nella mia risposta tra qualche minuto.
Arseni Mourzenko,

Vorrei di nuovo DV se potessi. La modifica peggiora le cose. Avere quattro diversi metodi per fare qualcosa in un modo nuovo è male, ma è colpa del ragazzo che aggiunge il secondo framework di registrazione. Di per sé non è male tracciare una linea nella sabbia e avere un codice pulito vicino al codice marcio.
RubberDuck,

@RubberDuck: non si tratta di aggiungere il secondo framework di registrazione, ma il primo. Il ragazzo che aggiunge il secondo framework di registrazione lo fa solo perché il primo viene utilizzato solo su una piccola funzionalità; se il codebase è stato refactored come consiglio, ciò non accadrebbe.
Arseni Mourzenko,

Penso che tu e io siamo d'accordo, ma le tue risposte si leggono in un modo che scoraggia il miglioramento. Non riesco proprio ad accettarlo.
RubberDuck,

3

Se stai facendo revisioni del codice come parte del tuo processo di sviluppo; quindi devi impostare le regole in base alle quali "giudichi" il codice che stai esaminando.

Questo dovrebbe entrare nella tua "definizione di fatto" e potrebbe essere una guida di stile, un documento di architettura per la base di codice o un'analisi statica, verificando i requisiti legali, qualunque cosa l'azienda decida siano i suoi requisiti di "qualità del codice"

Una volta che hai messo in atto le revisioni del codice, diventa una cosa ovvia, è stata seguita la guida di stile, abbiamo la percentuale richiesta di copertura del test ecc. Ecc.

Se non si dispone di questo, le revisioni del codice possono diventare una battaglia per chi ha il codice più grande o, come nella propria situazione, mettere in discussione le decisioni di giudizio sul refactoring rispetto alle funzionalità eseguite prima della scadenza. Questo è solo uno spreco di tempo per tutti.


3

Il tuo problema principale è che una revisione del codice su una nuova funzionalità significativa è il momento sbagliato per tenere questa discussione. A quel punto è troppo tardi per apportare modifiche tutt'altro che minori. Il posto giusto è nelle fasi di pianificazione o al più tardi in una revisione preliminare del progetto. Se la tua azienda non sta effettuando queste prime recensioni almeno in modo informale, dovresti prima cercare di cambiare quella cultura.

Il prossimo passo è farsi invitare a quegli incontri e avere idee produttive in quegli incontri. Principalmente ciò significa non cercare di cambiare tutto da un giorno all'altro, ma cercare pezzi di dimensioni ridotte che puoi isolare e affrontare. Quei pezzi si sommeranno significativamente nel tempo.

In altre parole, la chiave è suggerire regolarmente piccoli cambiamenti verso l'inizio dei progetti, piuttosto che essere abbattuti suggerendo cambiamenti più grandi verso la fine.


2

Nei luoghi in cui ho fatto la revisione del codice, accetterei e OK l'invio del codice, se si tratta dell'impegno di fare (almeno) del refactoring. Sia come un bug archiviato, come una storia o una promessa di inviare un'altra recensione con (alcuni) refactoring fatto.

In questi casi, se sono la persona che scrive il codice da rivedere, di solito preparo due modifiche, una con le mie nuove funzionalità o correzioni di bug, poi un'altra che ha quello E qualche ripulitura. In questo modo, le modifiche di pulizia non toglie nulla alle nuove funzionalità o alle correzioni di bug, ma viene facilmente indicato come token "sì, lo so che non risolve questi problemi, ma laggiù è" purificazione "che fa".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.