Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team

Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team
È facile provare un analizzatore di codice statico. Ma implementarlo, soprattutto nello sviluppo di un vecchio progetto di grandi dimensioni, richiede abilità. Se eseguito in modo errato, l’analizzatore può aggiungere lavoro, rallentare lo sviluppo e demotivare il team. Parliamo brevemente di come affrontare correttamente l'integrazione dell'analisi statica nel processo di sviluppo e iniziare a utilizzarla come parte di CI/CD.

Introduzione

Recentemente la mia attenzione è stata attirata dalla pubblicazione "Iniziare con l'analisi statica senza sovraccaricare il team". Da un lato, questo è un buon articolo che vale la pena leggere. D'altra parte, mi sembra che non fornisca ancora una risposta completa su come implementare indolore l'analisi statica in un progetto con molto di codice legacy. L'articolo dice che puoi accettare il debito tecnico e lavorare solo sul nuovo codice, ma non c'è risposta su cosa fare con questo debito tecnico in seguito.

Il nostro team di PVS-Studio offre il suo punto di vista su questo argomento. Diamo un'occhiata a come si pone in primo luogo il problema dell'implementazione di un analizzatore di codice statico, come superare questo problema e come eliminare gradualmente e indolore il debito tecnico.

Problemi

Di solito non è difficile avviare e vedere come funziona un analizzatore statico [1]. Potresti vedere errori interessanti o addirittura potenziali vulnerabilità spaventose nel codice. Puoi anche aggiustare qualcosa, ma poi molti programmatori si arrendono.

Tutti gli analizzatori statici producono falsi positivi. Questa è una caratteristica della metodologia di analisi statica del codice e non si può fare nulla al riguardo. Nel caso generale, questo è un problema irrisolvibile, come confermato dal teorema di Rice [2]. Anche gli algoritmi di apprendimento automatico non saranno di aiuto [3]. Anche se una persona non può sempre dire se questo o quel codice è sbagliato, non dovresti aspettartelo dal programma :).

I falsi positivi non sono un problema se l'analizzatore statico è già configurato:

  • Set di regole irrilevanti disabilitati;
  • Alcuni diagnostici irrilevanti sono stati disabilitati;
  • Se parliamo di C o C++, allora vengono marcate delle macro che contengono costrutti specifici che fanno apparire avvisi inutili in ogni luogo in cui vengono utilizzate tali macro;
  • Sono contrassegnate le proprie funzioni che eseguono azioni simili alle funzioni di sistema (il suo analogo memcpy o printf) [4];
  • I falsi positivi vengono specificatamente disabilitati utilizzando i commenti;
  • E così via.

In questo caso, possiamo aspettarci un basso tasso di falsi positivi di circa il 10-15% [5]. In altre parole, 9 avvisi su 10 dell'analizzatore indicheranno un problema reale nel codice, o almeno un "codice dall'odore forte". D'accordo, questo scenario è estremamente piacevole e l'analizzatore è un vero amico del programmatore.

Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team
In realtà, in un grande progetto, il quadro iniziale sarà completamente diverso. L'analizzatore emette centinaia o migliaia di avvisi per il codice legacy. È impossibile capire rapidamente quali di questi avvertimenti siano rilevanti e quali no. È irrazionale sedersi e iniziare a gestire tutti questi avvertimenti, poiché il lavoro principale in questo caso verrà interrotto per giorni o settimane. In genere, una squadra non può permettersi uno scenario del genere. Ci sarà anche un numero enorme di differenze che rovineranno la cronologia dei cambiamenti. E la rapida modifica di massa di così tanti frammenti nel codice comporterà inevitabilmente nuovi errori di battitura ed errori.

E, soprattutto, una simile impresa nella lotta contro gli avvertimenti non ha molto senso. Concordo sul fatto che, poiché il progetto funziona con successo da molti anni, la maggior parte degli errori critici in esso contenuti sono già stati corretti. Sì, queste correzioni erano molto costose, dovevano essere sottoposte a debug, ricevevano feedback negativi dagli utenti sui bug e così via. Un analizzatore statico aiuterebbe a correggere molti di questi errori in fase di codifica, in modo rapido ed economico. Ma al momento, in un modo o nell'altro, questi errori sono stati corretti e l'analizzatore rileva principalmente errori non critici nel vecchio codice. Questo codice potrebbe non essere utilizzato, potrebbe essere utilizzato molto raramente e un errore in esso contenuto potrebbe non portare a conseguenze evidenti. Forse da qualche parte l'ombra del pulsante è del colore sbagliato, ma ciò non interferisce con l'utilizzo del prodotto da parte di nessuno.

Naturalmente, anche gli errori più piccoli restano errori. E a volte un errore può nascondere una vera vulnerabilità. Tuttavia, rinunciare a tutto e passare giorni/settimane a occuparsi di difetti che a malapena si manifestano sembra un’idea dubbia.

I programmatori guardano, guardano, guardano tutti questi avvertimenti sul vecchio codice funzionante... E pensano: possiamo fare a meno dell'analisi statica. Andiamo a scrivere alcune nuove funzionalità utili.

A modo loro, hanno ragione. Pensano che prima debbano in qualche modo sbarazzarsi di tutti questi avvertimenti. Solo allora potranno beneficiare dell'uso regolare dell'analizzatore di codice. Altrimenti, i nuovi avvertimenti affogheranno semplicemente in quelli vecchi e nessuno presterà loro attenzione.

Questa è la stessa analogia degli avvisi del compilatore. Non per niente raccomandano di mantenere il numero di avvisi del compilatore su 0. Se ci sono 1000 avvisi, quando ce ne sono 1001, nessuno presterà attenzione e non è chiaro dove cercare questo avviso più recente.

Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team
La cosa peggiore in questa storia è se qualcuno dall'alto in questo momento ti costringe a utilizzare l'analisi statica del codice. Ciò non farà altro che demotivare la squadra, poiché dal loro punto di vista ci sarà un'ulteriore complessità burocratica che sarà solo d'intralcio. Nessuno guarderà i rapporti dell’analizzatore e tutto l’utilizzo sarà solo “sulla carta”. Quelli. Formalmente, l’analisi è integrata nel processo DevOps, ma in pratica ciò non avvantaggia nessuno. Abbiamo ascoltato storie dettagliate presso gli stand dei partecipanti alla conferenza. Un'esperienza di questo tipo può scoraggiare i programmatori dall'utilizzare strumenti di analisi statica per molto tempo, se non per sempre.

Implementazione ed eliminazione del debito tecnico

In effetti, non c’è nulla di difficile o spaventoso nell’introdurre l’analisi statica anche in un vecchio progetto di grandi dimensioni.

CI / CD

Inoltre, l'analizzatore può essere immediatamente inserito nel processo di sviluppo continuo. Ad esempio, la distribuzione PVS-Studio contiene utilità per visualizzare comodamente il rapporto nel formato desiderato e notifiche agli sviluppatori che hanno scritto sezioni problematiche del codice. Per coloro che sono più interessati ad avviare PVS-Studio dai sistemi CI/CD, consiglio di familiarizzare con il corrispondente sezione documentazione e una serie di articoli:

Ma torniamo alla questione del gran numero di falsi positivi nelle prime fasi di implementazione degli strumenti di analisi del codice.

Riparare il debito tecnico esistente e gestire i nuovi avvertimenti

I moderni analizzatori statici commerciali consentono di studiare solo i nuovi avvisi che compaiono nel codice nuovo o modificato. L'implementazione di questo meccanismo varia, ma l'essenza è la stessa. Nell'analizzatore statico PVS-Studio, questa funzionalità è implementata come segue.

Per iniziare rapidamente a utilizzare l'analisi statica, suggeriamo agli utenti di PVS-Studio di utilizzare il meccanismo per la soppressione di massa degli avvisi [6]. L'idea generale è la seguente. L'utente ha avviato l'analizzatore e ha ricevuto numerosi avvisi. Poiché un progetto in sviluppo da molti anni è vivo, si sviluppa e guadagna, molto probabilmente nel rapporto non ci saranno molti avvertimenti che indicano difetti critici. In altre parole, i bug critici sono già stati risolti in un modo o nell'altro utilizzando metodi più costosi o grazie al feedback dei clienti. Di conseguenza, tutto ciò che l'analizzatore rileva attualmente può essere considerato debito tecnico, che non è pratico cercare di eliminare immediatamente.

Puoi dire a PVS-Studio di considerare questi avvisi irrilevanti per ora (risparmiare il debito tecnico per dopo) e non li mostrerà più. L'analizzatore crea un file speciale in cui salva le informazioni sugli errori che non sono ancora interessanti. E ora PVS-Studio emetterà avvisi solo per il codice nuovo o modificato. Inoltre, tutto ciò è implementato in modo intelligente. Se, ad esempio, viene aggiunta una riga vuota all'inizio del file del codice sorgente, l'analizzatore capisce che, in realtà, non è cambiato nulla e continuerà a tacere. Questo file di markup può essere inserito in un sistema di controllo della versione. Il file è grande, ma questo non è un problema, poiché non ha senso archiviarlo spesso.

Ora tutti i programmatori vedranno avvisi relativi solo al codice nuovo o modificato. Pertanto, puoi iniziare a utilizzare l'analizzatore, come si suol dire, dal giorno successivo. E puoi tornare al debito tecnico in un secondo momento, correggere gradualmente gli errori e configurare l'analizzatore.

Quindi, il primo problema con l'implementazione dell'analizzatore in un vecchio progetto di grandi dimensioni è stato risolto. Ora scopriamo cosa fare con il debito tecnico.

Correzioni di bug e refactoring

La cosa più semplice e naturale è dedicare un po' di tempo all'analisi degli avvisi dell'analizzatore soppressi in modo massiccio e affrontarli gradualmente. Da qualche parte dovresti correggere gli errori nel codice, da qualche parte dovresti effettuare il refactoring per dire all'analizzatore che il codice non è problematico. Esempio semplice:

if (a = b)

La maggior parte dei compilatori e analizzatori C++ si lamentano di questo codice, poiché c'è un'alta probabilità che volessero effettivamente scriverlo (a == b). Ma c'è un accordo tacito, e questo di solito è annotato nella documentazione, secondo cui se ci sono parentesi aggiuntive, si ritiene che il programmatore abbia scritto deliberatamente tale codice e non è necessario giurare. Ad esempio, nella documentazione di PVS-Studio per la diagnostica V559 (CWE-481) è scritto chiaramente che sarà considerata corretta e sicura la seguente riga:

if ((a = b))

Un altro esempio. È dimenticato in questo codice C++? rompere o no?

case A:
  foo();
case B:
  bar();
  break;

L'analizzatore PVS-Studio emetterà un avviso qui V796 (CWE-484). Questo potrebbe non essere un errore, nel qual caso dovresti dare un suggerimento al parser aggiungendo l'attributo [[sfumare]] o per esempio __attributo__((fallito)):

case A:
  foo();
  [[fallthrough]];
case B:
  bar();
  break;

Si può dire che tali modifiche al codice non risolvono il bug. Sì, questo è vero, ma fa due cose utili. In primo luogo, il rapporto dell’analizzatore elimina i falsi positivi. In secondo luogo, il codice diventa più comprensibile per le persone coinvolte nella sua manutenzione. E questo è molto importante! Solo per questo vale la pena effettuare piccoli refactoring per rendere il codice più chiaro e più facile da mantenere. Poiché l’analizzatore non capisce se è necessaria o meno la “pausa”, ciò non sarà chiaro nemmeno agli altri programmatori.

Oltre alle correzioni di bug e ai refactoring, è possibile eliminare in modo specifico gli avvisi evidentemente falsi dell'analizzatore. È possibile disabilitare alcune diagnostiche irrilevanti. Ad esempio, qualcuno pensa che gli avvertimenti siano inutili V550 sul confronto dei valori float/double. E alcuni li classificano come importanti e meritevoli di studio [7]. Spetta al team di sviluppo decidere quali avvisi siano considerati rilevanti e quali no.

Esistono altri modi per eliminare i falsi allarmi. Ad esempio, il markup macro è stato menzionato in precedenza. Tutto questo è descritto più dettagliatamente nella documentazione. La cosa più importante è capire che se ti avvicini gradualmente e sistematicamente al lavoro con i falsi positivi, non c'è niente di sbagliato in loro. La stragrande maggioranza degli avvisi poco interessanti scompare dopo la configurazione e rimangono solo i luoghi che richiedono davvero uno studio attento e alcune modifiche al codice.

Inoltre, aiutiamo sempre i nostri clienti a configurare PVS-Studio in caso di difficoltà. Inoltre, ci sono stati casi in cui noi stessi abbiamo eliminato i falsi avvisi e corretto gli errori [8]. Per ogni evenienza, ho deciso di menzionare che è possibile anche questa opzione per una cooperazione estesa :).

Metodo a cricchetto

Esiste un altro approccio interessante per migliorare gradualmente la qualità del codice eliminando l'avviso dell'analizzatore statico. Il punto è che il numero di avvisi non può che diminuire.

Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team

Viene registrato il numero di avvisi emessi dall'analizzatore statico. Il Quality Gate è configurato in modo tale che ora puoi solo inserire un codice che non aumenti il ​​numero di operazioni. Di conseguenza, il processo di riduzione graduale del numero di allarmi inizia regolando l'analizzatore e correggendo gli errori.

Anche se una persona vuole imbrogliare un po' e decide di oltrepassare il cancello della qualità non eliminando gli avvertimenti nel suo nuovo codice, ma migliorando il vecchio codice di terze parti, questo non fa paura. Tuttavia, il cricchetto ruota in una direzione e gradualmente il numero di difetti diminuirà. Anche se una persona non vuole correggere i propri nuovi difetti, dovrà comunque migliorare qualcosa nel codice adiacente. Ad un certo punto, i modi più semplici per ridurre il numero di avvisi finiscono e arriva un punto in cui i bug reali verranno risolti.

Questa metodologia è descritta più dettagliatamente in un articolo molto interessante di Ivan Ponomarev "Implementa l'analisi statica nel processo, anziché utilizzarla per trovare bug", che consiglio di leggere a chiunque sia interessato a migliorare la qualità del codice.

L'autore dell'articolo ha anche un rapporto su questo argomento: "Analisi statica continua".

conclusione

Spero che dopo questo articolo i lettori accetteranno maggiormente gli strumenti di analisi statica e vorranno implementarli nel processo di sviluppo. Se avete domande, siamo sempre pronti consigliare utenti del nostro analizzatore statico PVS-Studio e assistenza nella sua implementazione.

Ci sono altri dubbi tipici sul fatto che l’analisi statica possa davvero essere conveniente e utile. Ho cercato di dissipare la maggior parte di questi dubbi nella pubblicazione “Motivi per introdurre l'analizzatore di codice statico PVS-Studio nel processo di sviluppo” [9].

Grazie per l'attenzione e venite scaricare e prova l'analizzatore PVS-Studio.

Collegamenti aggiuntivi

  1. Andrej Karpov. Come posso visualizzare rapidamente gli avvisi interessanti prodotti dall'analizzatore PVS-Studio per il codice C e C++?
  2. Wikipedia. Il teorema di Rice.
  3. Andrey Karpov, Victoria Khanieva. Utilizzo dell'apprendimento automatico nell'analisi statica del codice sorgente del programma.
  4. PVS-Studio. Documentazione. Impostazioni diagnostiche aggiuntive.
  5. Andrej Karpov. Caratteristiche dell'analizzatore PVS-Studio utilizzando l'esempio delle librerie EFL Core, 10-15% di falsi positivi.
  6. PVS-Studio. Documentazione. Soppressione di massa dei messaggi dell'analizzatore.
  7. Ivan Andryashin. Su come abbiamo testato l'analisi statica sul nostro progetto di un simulatore didattico di chirurgia endovascolare a raggi X.
  8. Pavel Eremeev, Svyatoslav Razmyslov. Come il team di PVS-Studio ha migliorato il codice dell'Unreal Engine.
  9. Andrej Karpov. Motivi per introdurre l'analizzatore di codice statico PVS-Studio nel processo di sviluppo.

Come implementare un analizzatore di codice statico in un progetto legacy senza demotivare il team

Se vuoi condividere questo articolo con un pubblico di lingua inglese, utilizza il link di traduzione: Andrey Karpov. Come introdurre un analizzatore di codice statico in un progetto legacy e non scoraggiare il team.

Fonte: habr.com

Aggiungi un commento