Come si misura in modo significativo la manutenibilità?


23

Contesto: sono uno sviluppatore aziendale in un negozio tutto MS.

Qualcuno può raccomandare un buon modo per misurare oggettivamente la manutenibilità di un pezzo di codice o di un'applicazione?

Perché manutenibilità : sono stanco delle metriche "di qualità" nel mio gruppo che ruotano solo intorno al numero di bug e alla copertura del codice. Entrambe le metriche sono facili da giocare, soprattutto quando non si misura la manutenibilità. La miopia e le scadenze si traducono in enormi debiti tecnici che non vengono mai realmente affrontati.

Perché la capacità di misurare oggettivamente : lavoro in un grande gruppo aziendale. Se non riesci a misurarlo oggettivamente, non puoi ritenere le persone responsabili o farle migliorare. Le misurazioni soggettive non si verificano o non si verificano in modo coerente.

Sto esaminando le metriche del codice VS2010 , ma mi chiedo se qualcuno ha altri consigli.


@Anon - Sono d'accordo, ma almeno mi darebbe un punto di partenza. In questo momento non c'è niente; non deve nemmeno essere giocato.
nlawalker,

1
Davvero non vedo come si possa fare questo senza revisioni del codice dei pari. Qualcuno ha bisogno di capire veramente la progettazione generale del sistema (e uno deve esistere) per guardare un'unità di codice e andare ... hm questo potrebbe essere migliorato da un design migliorato o questa è la ripetizione del codice o un buon signore i tuoi strumenti sono obsoleti ... In una nota simile, potresti mantenere linee guida generali come "hey ragazzi non è una buona idea codificare gli indici in visualizzazioni di griglia, usare invece gli itemtemplate e selezionare le colonne per nome". Quando si tratta di esso, gli sviluppatori devono solo essere bravi e in squadra. Da Vinci non può insegnare meraviglia.
P.Brian.Mackey,

8
Se disponi di metriche di gioco per gli sviluppatori invece di scrivere già un buon codice, l'aggiunta di altre metriche comporterà anche il loro utilizzo di tali metriche, ma non risolverà il problema . La soluzione è eliminare completamente le metriche e utilizzare altri mezzi (ad esempio recensioni di codice pubblico) per garantire la qualità del codice.
Anon.

3
"Tutto ciò che può essere contato non conta necessariamente; tutto ciò che conta non può essere necessariamente contato." -Einstein
Jason Baker,

@nlawalker Oltre ai problemi già posti dai rispondenti, la tua domanda è caricata con un'ipotesi discutibile, che se esistesse tale misurazione le persone potrebbero fare qualcosa al riguardo. La bassa manutenibilità è il risultato di vari fattori esterni al software stesso: quanto sia difficile o ben definito il problema che il programma tenta di risolvere, l'esperienza del personale, il fatturato, i tempi di consegna del mercato, i cambiamenti di ambito ... semplicemente non si può mettere una taglia su questo aspetto il problema è una questione di buona volontà.
Arthur Havlicek,

Risposte:


7

L'avvertenza con la misurazione della manutenibilità è che si sta tentando di prevedere il futuro. Copertura del codice, conteggio dei bug, LOC, complessità ciclomatica si occupano tutti del presente .

La realtà è che a meno che tu non abbia prove concrete del fatto che il codice non sia mantenibile come lo è ... risolvendo un bug ha causato N quantità di ore di tempo non necessario a causa di codice non mantenibile; quindi avere una gamba su cui stare sarà intrinsecamente difficile. In questo esempio potrebbe essere stato causato dal fatto che una metodologia troppo complessa è stata utilizzata quando sarebbe bastato qualcosa di molto più semplice. Avventurarsi in un'area in cui si tenta di misurare metodologie, paradigmi e migliori pratiche diventa sempre più difficile con guadagni a lungo termine.

Percorrere questa strada è purtroppo una strada verso il nulla. Concentrati sulla scoperta di problemi di root che hanno un merito sostanziale e non sono legati ai sentimenti personali su un problema come la mancanza di convenzioni di denominazione attraverso la base di codice e trova un modo per misurare il successo e gli insuccessi attorno a quel problema di root. Ciò ti consentirà quindi di iniziare a mettere insieme un insieme di blocchi da cui è quindi possibile iniziare a formulare soluzioni in giro.


7

Bene, la misura che uso, o mi piace pensare di usare, è questa:

Per ogni requisito funzionale indipendente, singolo, a una riga, take-it-or-Leave-it, eseguire l'istantanea della base di codice prima di implementarla. Quindi implementalo, incluso trovare e correggere eventuali bug introdotti nel processo. Quindi eseguire un difftra la base di codice prima e dopo. Il diffvi mostrerà un elenco di tutti gli inserimenti, cancellazioni e le modifiche che ha implementato il cambiamento. (Come inserire 10 righe di codice consecutive è una modifica.) Quante modifiche ci sono state? Più piccolo è quel numero, in genere, più il codice è gestibile.

La chiamo ridondanza del codice sorgente, perché è come la ridondanza di un codice che corregge gli errori. Le informazioni erano contenute in 1 blocco, ma sono state codificate come blocchi N, che devono essere fatti tutti insieme, per essere coerenti.

Penso che questa sia l'idea alla base di DRY, ma è un po 'più generale. Il motivo per cui è buono che quel conteggio sia basso, se N richiede 1 modifiche per implementare un requisito tipico, e come programmatore fallibile ottieni inizialmente solo N-1 o N-2, hai inserito 1 o 2 bug. Oltre allo sforzo di programmazione O (N), questi bug devono essere scoperti, individuati e riparati. Ecco perché la piccola N è buona.

Manutenibile non significa necessariamente leggibile, per un programmatore che non ha imparato come funziona il codice. L'ottimizzazione di N potrebbe richiedere alcune cose che creano una curva di apprendimento per i programmatori. Ecco un esempio Una cosa che aiuta è se il programmatore tenta di anticipare i cambiamenti futuri e lascia indicazioni pratiche nel commento del programma.

Penso che quando N viene ridotto abbastanza lontano (l'ottimale è 1) il codice sorgente legge più come un linguaggio specifico del dominio (DSL). Il programma non "risolve" tanto il problema in quanto "indica" il problema, perché idealmente ogni requisito è semplicemente riformulato come un singolo pezzo di codice.

Sfortunatamente, non vedo persone che imparano molto a farlo. Piuttosto sembrano pensare che i sostantivi mentali dovrebbero diventare classi e i verbi diventino metodi e tutto ciò che devono fare è girare la manovella. Ciò si traduce in codice con N di 30 o più, nella mia esperienza.


Non è forse un presupposto molto grande: tutti i requisiti funzionali hanno all'incirca le stesse dimensioni? E questa metrica non scoraggia la separazione delle responsabilità? Devo implementare una funzione orizzontale; il codice più "gestibile" è quindi una riscrittura quasi totale di un programma che è interamente contenuto in un metodo monolitico.
Aaronaught il

@Aaronaught: Non so quanto sia grandioso, ma nel nostro gruppo lavoriamo su elenchi di requisiti / funzionalità, alcuni interdipendenti, altri no. Ognuno ha una descrizione relativamente breve. Se ci vuole una riscrittura importante, sicuramente ho visto / fatto quelli, ma mi dice che probabilmente c'era un modo migliore per organizzare il codice. Questo è il mio esempio canonico. Non dico che è facile da imparare, ma una volta appreso risparmia una grande quantità misurabile di sforzi, ottenendo modifiche rapidamente e senza errori.
Mike Dunlavey,

5

La manutenibilità non è poi così misurabile. È una visione soggettiva di un individuo basata sulle sue esperienze e preferenze.

Per dare un pezzo di codice, ti viene l'idea di un design perfetto .

Quindi, per qualsiasi deviazione del codice reale da quello perfetto, diminuisci il valore di 100 di un numero. Da ciò che dipende esattamente dalle conseguenze di un approccio non perfetto scelto.

Un esempio:

Un pezzo di codice legge e importa alcuni formati di dati e potrebbe mostrare un messaggio di errore se qualcosa non va.

Una soluzione perfetta (100) avrebbe messaggi di errore conservati in un luogo comune. Se la tua soluzione li ha hardcoded come costanti di stringa direttamente nel codice, prendi, diciamo 15 di sconto. Quindi il tuo indice di manutenibilità diventa 85.


4

Un risultato del codice difficile da mantenere è che impiegherà più tempo (in media) a correggere i bug. Quindi, a prima vista, una metrica sembrerebbe essere il tempo impiegato per correggere un bug da quando viene assegnato (cioè la correzione viene avviata) a quando è "pronto per il test".

Ora, funzionerà davvero solo dopo aver corretto un numero ragionevole di bug per ottenere il tempo "medio" (che cosa significhi mai). Non è possibile utilizzare la figura per un determinato bug in quanto la difficoltà di rintracciare non dipende solo dalla "manutenibilità" del codice.

Naturalmente, man mano che correggi più bug, il codice diventa "più facile" da gestire man mano che lo stai migliorando (o almeno dovresti essere) e stai acquisendo maggiore familiarità con il codice. Contrariamente a ciò, i bug tenderanno a essere più oscuri e quindi ancora più difficili da rintracciare.

Ciò soffre anche del problema che se le persone tenderanno a correre correzioni di bug per ottenere un punteggio più basso, causando così nuovi bug o non risolvendo correttamente quello esistente portando a ancora più lavoro e forse anche a un codice peggiore.


2

Trovo che le metriche del codice di Visual Studio siano abbastanza decenti per fornire una rapida metrica di "manutenibilità". Vengono acquisite 5 metriche principali:

  • Complessità ciclomatica
  • Profondità di eredità
  • Class Couling
  • Linee di codice (per metodo, per classe, per progetto, qualunque sia, a seconda del livello di roll-up)

L'indice di manutenibilità è quello che trovo utile. È un indice composito, basato su:

  1. Dimensione totale (righe di codice)
  2. N. di classi o file
  3. Numero di metodi
  4. Complessità ciclomatica superiore a 20 (o 10 - configurabile, 10 è la mia preferenza)
  5. Duplicazione

Occasionalmente vado a vedere i miei metodi con un indice di manutenibilità basso (basso = cattivo per questo). Quasi a colpo sicuro, i metodi nel mio progetto con l'indice di manutenibilità più basso sono quelli che hanno più bisogno di una riscrittura e i più difficili da leggere (o mantenere).

Vedi il white paper per maggiori informazioni sui calcoli.


1

Due che saranno significativi sono la complessità ciclomatica e l'accoppiamento di classe. Non puoi eliminare la complessità, tutto ciò che puoi fare è dividerlo in pezzi gestibili. Queste 2 misure dovrebbero darti un'idea di dove può essere posizionato il codice difficile da mantenere, o almeno dove cercare il più difficile.

La complessità ciclomatica è una misura di quanti percorsi ci sono nel codice. Ogni percorso dovrebbe essere testato (ma probabilmente non lo è). Qualcosa con una complessità superiore a circa 20 dovrebbe essere suddiviso in moduli più piccoli. Un modulo con una complessità cycomatic di 20 (si potrebbe duplicare questo con 20 if then elseblocchi successivi ) avrà un limite superiore di 2 ^ 20 percorsi da testare.

L'accoppiamento di classe è una misura di quanto le classi siano strettamente legate. Un esempio di codice errato con cui ho lavorato presso il mio precedente datore di lavoro include un componente "livello dati" con circa 30 elementi nel costruttore. La persona per lo più "responsabile" per quel componente ha continuato ad aggiungere parametri di livello aziendale e UI al costruttore / a chiamate aperte fino a quando non è stata una grande palla di fango. Se la memoria mi serve correttamente, c'erano circa 15 diverse chiamate nuove / aperte (alcune non più utilizzate), tutte con set di parametri leggermente diversi. Abbiamo istituito revisioni del codice al solo scopo di impedirgli di fare più cose come questa - e per evitare di far sembrare che lo stessimo individuando, abbiamo rivisto il codice di tutti nel team, quindi abbiamo perso circa mezza giornata per 4-6 persone ogni giorno perché non abbiamo


2
Avere recensioni di codice per tutti non è una brutta cosa, onestamente. Potresti sentirti come se stessi perdendo tempo, ma a meno che tutti non lo stiano usando come una scusa per rilassarti, dovresti ottenere informazioni preziose da loro.
Anon.

1

In sostanza, la manutenibilità può davvero essere misurata solo dopo che è stata richiesta, non prima . Cioè, puoi solo dire se un pezzo di codice è gestibile, quando devi mantenerlo.

È relativamente ovvio misurare quanto sia stato facile adattare un pezzo di codice alle mutevoli esigenze. È quasi impossibile misurare in anticipo, come risponderà ai cambiamenti nei requisiti. Ciò significherebbe che devi prevedere cambiamenti nei requisiti. E se riesci a farlo, dovresti ottenere un prezzo nobile;)

L'unica cosa che puoi fare è concordare con il tuo team, su una serie di regole concrete (come i principi SOLID), che tutti credono che in generale aumenti la manutenibilità.
Se i principi sono ben scelti (penso che andare con SOLID sia una buona scelta per cominciare), puoi dimostrare chiaramente che sono stati violati e ritenere gli autori responsabili di ciò.
Avrai un momento molto difficile, cercando di promuovere una misura assoluta per la manutenibilità, convincendo in modo incrementale il tuo team a attenersi a una serie concordata di principi stabiliti realistici.


1

enormi quantità di debito tecnico che non vengono mai realmente affrontate

Che dire del debito tecnico "superato dagli eventi"?

Scrivo un codice scadente e lo accingo alla produzione.

Si osserva - correttamente - che non è mantenibile.

Tale codice, tuttavia, è l'ultimo round di funzionalità per una linea di prodotti che verrà ritirata dal servizio perché il contesto legale è cambiato e la linea di prodotti non ha futuro.

Il "debito tecnico" è eliminato da una modifica legislativa che rende tutto obsoleto.

La metrica "manutenibilità" è passata da "cattiva" a "irrilevante" a causa di considerazioni esterne.

Come può essere misurato?


"Tra cento anni saremo tutti morti e niente di tutto questo avrà importanza. In un certo senso si mettono le cose in prospettiva, no?" Se c'è qualcosa di irrilevante, è questa risposta che non è una risposta alla domanda.
Martin Maat,

0

La prossima cosa migliore per le revisioni di un codice peer è creare un'architettura funzionante prima di codificare un'unità o un prodotto. Il rifrattore rosso-verde è un modo abbastanza semplice per farlo. Chiedi a un ragazzo senior di mettere insieme un'interfaccia praticabile e dividere il lavoro. Tutti possono prendere il loro pezzo del puzzle e raggiungere la vittoria in rosso-verde. Dopo questo, una revisione e un refactor del codice peer sarebbero in ordine. Questo ha funzionato piuttosto male su un grande prodotto passato a cui ho lavorato.


0

Questionario

Che ne dici di fare un questionario anonimo per gli sviluppatori, da compilare una volta al mese? Le domande andrebbero qualcosa del tipo:

  • Quanto tempo hai trascorso nell'ultimo mese nel progetto X (circa) [0% ... 100%]
  • Come giudicheresti lo stato della base di codice in termini di manutenibilità [veramente mediocre, mediocre, neutro, okay, buono, veramente buono].
  • Quanto sarebbe complessa la base di codice rispetto alla complessità del progetto [troppo complesso, giusto, troppo semplificato].
  • Quante volte ti sei sentito ostacolato nel risolvere i tuoi compiti a causa dell'eccessiva complessità della base di codice? [niente affatto, una volta ogni tanto, spesso, costantemente].

(Sentiti libero di aggiungere ulteriori domande che ritieni possano essere utili per misurare la manutenibilità nei commenti e le aggiungerò.)


0

Posso pensare a due modi di considerare la manutenibilità (sono sicuro che ci sono più speranze che altri possano trovare buone definizioni.

Modifica senza comprensione.

Può un bug fixer entrare nel codice e risolvere un problema senza dover capire come funziona l'intero sistema.

Ciò può essere ottenuto fornendo test unitari completi (test di regressione). Dovresti essere in grado di verificare che qualsiasi modifica al sistema non cambi il comportamento del sistema con qualsiasi input valido specifico.

In questa situazione, un bug fixer dovrebbe essere in grado di entrare e correggere un bug (semplice) con una conoscenza minima del sistema. Se la correzione funziona, nessuno dei test di regressione dovrebbe fallire. Se i test di regressione falliscono, è necessario passare alla fase 2.

maintainabilty1 = K1 . (Code Coverage)/(Coupling of Code) * (Complexity of API)

Modifica con comprensione.

Se una correzione di bug diventa non banale e devi capire il sistema. Allora, com'è la documentazione del sistema. Stiamo Non parlando la documentazione delle API esterna (sono relativamente inutili). Quello che dobbiamo capire è come funziona il sistema in cui trucchi intelligenti (leggi hack) vengono utilizzati nelle implementazioni ecc.

Ma la documentazione non è sufficiente, il codice deve essere chiaro e comprensibile. Per misurare la comprensibilità di un codice possiamo usare un piccolo trucco. Dopo che lo sviluppatore ha terminato la programmazione, dagli un mese per lavorare su qualcos'altro. Quindi chiedi loro di tornare e documentare il sistema in modo tale che un molo possa ora comprendere il sistema. Se il codice è relativamente facile da capire, dovrebbe essere veloce. Se è scritto male, ci vorrà più tempo per capire cosa hanno costruito e scrivere la documentazione.

Quindi forse potremmo escogitare qualche misura di questo:

maintainability2 = K2 . (Size of doc)/(Time to write doc)

0

Trovo spesso che la soluzione "il più breve equivalente" tende ad essere più mantenibile.

Qui il più breve indica il minor numero di operazioni (non linee). E equivalente significa che la soluzione più breve non dovrebbe avere una complessità temporale o spaziale peggiore rispetto alla soluzione precedente.

Ciò significa che tutti gli schemi ripetitivi logicamente simili dovrebbero essere estratti nell'astrazione appropriata: blocchi di codice simili? Estrai per funzionare. Variabili che sembrano verificarsi insieme? Estraili in una struttura / classe. Classi i cui membri differiscono solo per tipo? Hai bisogno di un generico. Sembri ricalcolare la stessa cosa in molti posti? Calcola all'inizio e memorizza il valore in una variabile. Ciò comporterà un codice più breve. Questo è fondamentalmente il principio DRY.

Possiamo anche concordare che le astrazioni inutilizzate debbano essere eliminate: classi, funzioni che non sono più necessarie sono codice morto, quindi dovrebbero essere rimosse. Il controllo della versione ricorderà se dovremo mai ripristinarlo.

Ciò che viene spesso discusso sono le astrazioni a cui si fa riferimento una sola volta: funzioni non di callback che vengono chiamate una sola volta senza motivo di essere mai chiamate più di una volta. Un generico che viene istanziato usando solo un tipo e non c'è motivo per cui verrà mai istanziato con un altro tipo. Interfacce che vengono implementate una sola volta e non vi è alcuna reale ragione per cui sarebbero mai state implementate da qualsiasi altra classe e così via. La mia opinione è che queste cose non sono necessarie e dovrebbero essere rimosse, questo è fondamentalmente il principio YAGNI.

Quindi dovrebbe esserci uno strumento in grado di individuare la ripetizione del codice, ma penso che il problema sia simile alla ricerca della compressione ottimale, che è il problema della complessità di Kolmogorov che è indecidibile. Ma dall'altra parte le astrazioni inutilizzate e sotto utilizzate sono facili da individuare in base al numero di riferimenti: un controllo per questo può essere automatizzato.


0

È tutto soggettivo e qualsiasi misurazione basata sul codice stesso è in definitiva irrilevante. Alla fine dipende dalla tua capacità di soddisfare le richieste. Puoi ancora fornire le funzionalità che ti vengono richieste e, se puoi, con quale frequenza ti torneranno quei cambiamenti perché qualcosa non è ancora del tutto giusto e quanto gravi sono questi problemi?

Ho appena (ri) definito la manutenibilità ma è ancora soggettiva. D'altra parte, potrebbe non importare molto. Dobbiamo solo soddisfare i nostri clienti e goderne, ecco a cosa puntiamo.

Apparentemente senti di dover dimostrare al tuo capo o ai tuoi colleghi che bisogna fare qualcosa per migliorare lo stato della base di codice. Direi che dovrebbe essere sufficiente per te dire che sei frustrato dal fatto che per ogni piccola cosa che devi modificare o aggiungere devi risolvere o risolvere altri 10 problemi che avrebbero potuto essere evitati. Quindi nomina un'area nota e crea un caso per capovolgerlo. Se ciò non genera alcun supporto nella tua squadra, potresti stare meglio da qualche altra parte. Se alle persone intorno a te non importa, dimostrare che il tuo punto non cambierà idea comunque.

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.