Implementa l'analisi statica in u prucessu, piuttostu cà aduprà per truvà bug

Aghju statu incitatu à scrive stu articulu da a grande quantità di materiali nantu à l'analisi statica chì sò sempre più venuti à a mo attenzione. Prima, questu PVS-studio blog, chì attivamente prumove in Habré cù l'aiutu di recensioni di errori truvati da u so strumentu in i prughjetti open source. Recentemente PVS-studio implementatu Supportu Java, è, sicuru, i sviluppatori di IntelliJ IDEA, chì u so analizzatore integratu hè probabilmente u più avanzatu per Java oghje, ùn pudia stà luntanu.

Quandu leghje tali recensioni, avete a sensazione chì parlemu di un elixir magicu: appughjà u buttone, è quì hè - una lista di difetti davanti à i vostri ochji. Sembra chì cum'è l'analizzatori migliurà, più è più bug seranu automaticamente truvati, è i prudutti scanati da questi robots diventeranu megliu è megliu, senza alcunu sforzu da a nostra parte.

Ma ùn ci sò micca elisirs magichi. Vogliu parlà di ciò chì di solitu ùn si parla micca in i posti cum'è "eccu e cose chì u nostru robot pò truvà": ciò chì l'analizzatori ùn ponu micca fà, quale hè u so veru rolu è u locu in u prucessu di consegna di u software, è cumu implementà bè. .

Implementa l'analisi statica in u prucessu, piuttostu cà aduprà per truvà bug
Ratchet (fonte: Wikipedia).

Ciò chì l'analizzatori statici ùn ponu mai fà

Chì ghjè l'analisi di u codice fonte, da un puntu di vista praticu? Avemu furnisce un pocu di codice fonte cum'è input, è cum'è output, in pocu tempu (assai più brevi di e teste in esecuzione) ottenemu qualchì infurmazione nantu à u nostru sistema. A limitazione fundamentale è matematicamente insurmontable hè chì pudemu ottene solu una classa abbastanza stretta di informazioni in questu modu.

L'esempiu più famosu di un prublema chì ùn pò micca esse risoltu cù l'analisi statica hè prublema di arrestu: Questu hè un teorema chì prova chì hè impussibile di sviluppà un algoritmu generale chì pò determinà da u codice fonte di un prugramma s'ellu ci sarà un ciclu o finisce in un tempu finitu. Una estensione di stu teorema hè Teorema di Rice, chì dichjara chì per qualsiasi pruprietà non-triviale di funzioni computabili, determinà se un prugramma arbitrariu evaluate una funzione cù una tale pruprietà hè un prublema algoritmicamente intrattabile. Per esempiu, hè impussibile di scrive un analizatore chì pò determinà da qualsiasi codice fonte se u prugramma chì hè analizatu hè una implementazione di un algoritmu chì calcula, per dì, u squaring di un entero.

Cusì, a funziunalità di l'analizzatori statici hà limitazioni insurmountable. Un analizzatore staticu ùn serà mai capace di detectà in tutti i casi cose cum'è, per esempiu, l'occurrence di una "eccezzioni di puntatore nulla" in lingue chì permettenu u valore di null, o in tutti i casi per determinà l'occurrence di un " attributu micca truvatu" in lingue dinamicamente tipizzate. Tuttu ciò chì l'analizzatore staticu più avanzatu pò fà hè di mette in risaltu casi spiciali, u numeru di quale, trà tutti i prublemi pussibuli cù u vostru codice fonte, hè, senza esagerazione, una goccia in l'oceanu.

L'analisi statica ùn hè micca di truvà bug

Da quì sopra, a cunclusione segue: l'analisi statica ùn hè micca un mezzu per riduce u numeru di difetti in un prugramma. Mi azzarderaghju à dì: quandu hè appiicatu à u vostru prughjettu per a prima volta, truverà posti "interessanti" in u codice, ma, assai prubabile, ùn truverà micca difetti chì affettanu a qualità di u vostru prugramma.

L'esempii di difetti truvati automaticamente da l'analizzatori sò impressiunanti, ma ùn devemu micca scurdate chì questi esempi sò stati truvati scannendu un grande settore di grande codebases. Da u listessu principiu, i pirate chì anu l'uppurtunità di pruvà parechje password simplici nantu à un gran numaru di cunti eventualmente truvà quelli cunti chì anu una password simplice.

Questu significa chì l'analisi statica ùn deve esse usata? Di sicuru micca ! È per esattamente u listessu mutivu chì vale a pena cuntrollà ogni nova password per assicurà chì hè inclusa in a lista di stop di password "semplici".

L'analisi statica hè più cà truvà bug

In fatti, i prublemi praticamenti risolti da l'analisi sò assai più largu. Dopu tuttu, in generale, l'analisi statica hè ogni verificazione di i codici fonte realizata prima di esse lanciata. Eccu alcune cose chì pudete fà:

  • Cuntrollà u stilu di codificazione in u sensu più largu di a parolla. Questu include sia a verificazione di u furmatu, a ricerca di l'usu di parentesi vacanti / extra, a fissazione di soglie nantu à metriche cum'è u numeru di linee / a cumplessità ciclomatica di un metudu, ecc. In Java, un tali strumentu hè Checkstyle, in Python - flake8. I prugrammi di sta classa sò generalmente chjamati "linters".
  • Ùn solu u codice eseguibile pò esse analizatu. I schedarii di risorse cum'è JSON, YAML, XML, .properties ponu (è deve!) esse verificatu automaticamente per a validità. Dopu tuttu, hè megliu à scopre chì a struttura JSON hè rotta per via di qualchi virgulette unpaired in una prima fase di verificazione automatica Pull Request cà durante l'esekzione di prova o u tempu di esecuzione? Strumenti apprupriati sò dispunibili: per esempiu. YAMLint, JSONLint.
  • A compilazione (o parsing per i linguaggi di prugrammazione dinamica) hè ancu un tipu d'analisi statica. In generale, i compilatori sò capaci di pruduce avvisi chì indicanu prublemi cù a qualità di u codice fonte è ùn deve esse ignoratu.
  • A volte a compilazione hè più cà solu compilazione di codice eseguibile. Per esempiu, sè vo avete documentazione in u furmatu AsciiDoctor, dopu à u mumentu di trasfurmà in HTML/PDF u gestore AsciiDoctor (Plugin Maven) pò emette avvirtimenti, per esempiu, nantu à i ligami interni rotti. È questu hè un bonu mutivu per ùn accettà a Pull Request cù cambiamenti di documentazione.
  • A verificazione ortografica hè ancu un tipu d'analisi statica. Utilità aspell hè capace di verificà l'ortografia micca solu in a documentazione, ma ancu in i codici fonte di u prugramma (cumenti è letterali) in diverse lingue di prugrammazione, cumpresi C/C++, Java è Python. Un errore di ortografia in l'interfaccia d'utilizatore o a documentazione hè ancu un difettu!
  • Testi di cunfigurazione (circa ciò chì sò - vede. questu и questu rapporti), anche eseguiti in un runtime di test di unità cum'è pytest, sò in fatti ancu un tipu d'analisi statica, postu chì ùn eseguisce micca codici fonte durante a so eseguzione.

Comu pudete vede, a ricerca di bug in questa lista ghjoca u rolu menu impurtante, è tuttu l'altru hè dispunibule cù l'uttene open source gratuiti.

Qualessu di sti tipi di analisi statica duvete aduprà in u vostru prughjettu? Di sicuru, più u megliu! A cosa principal hè di implementà currettamente, chì serà discutitu in più.

Pipeline di consegna cum'è filtru multi-stadiu è analisi statica cum'è a so prima tappa

A metafora classica per l'integrazione cuntinua hè un pipeline attraversu quale u flussu di cambiamenti, da i cambiamenti di codice fonte à a consegna à a produzzione. A sequenza standard di tappe in questa pipeline hè cusì:

  1. analisi statica
  2. compilation
  3. test di unità
  4. testi di integrazione
  5. Testi di UI
  6. verificazione manuale

I cambiamenti rifiutati in u stadiu Nth di u pipeline ùn sò micca trasferiti à u stadiu N + 1.

Perchè esattamente questu modu è micca altrimenti? In a parte di prova di u pipeline, i testatori ricunnosceranu a famosa piramide di prova.

Implementa l'analisi statica in u prucessu, piuttostu cà aduprà per truvà bug
Pruvate a piramide. Fonte: un articulu Martin Fowler.

À u fondu di sta piramide sò testi chì sò più faciuli di scrive, più veloce per eseguisce, è ùn anu micca tendenza à fallu. Dunque, ci deve esse più di elli, si deve copre più codice è esse eseguitu prima. À a cima di a piramide, u cuntrariu hè veru, cusì u numeru di teste di integrazione è UI deve esse ridutta à u minimu necessariu. A persona in questa catena hè a risorsa più caru, lenta è inaffidabile, cusì hè à a fine è solu eseguisce u travagliu se i tappe previ ùn anu micca truvatu difetti. Tuttavia, i stessi principii sò usati per custruisce un pipeline in parti micca direttamente ligati à a prova!

Vogliu offre una analogia in a forma di un sistema di filtrazione d'acqua multi-stadi. L'acqua brutta (cambia cù difetti) hè furnita à l'input; à a pruduzzioni duvemu riceve acqua pulita, in quale tutti i contaminanti indesiderati sò stati eliminati.

Implementa l'analisi statica in u prucessu, piuttostu cà aduprà per truvà bug
Filtru multi-stadi. Fonte: Wikipedia

Comu sapete, i filtri di pulizia sò pensati in modu chì ogni cascata successiva pò filtrà una frazzioni sempre più fine di contaminanti. À u listessu tempu, e cascate di purificazione più grossa anu un rendimentu più altu è un costu più bassu. In a nostra analogia, questu significa chì e porte di qualità di input sò più veloci, necessitanu menu sforzu per inizià, è sò elli stessi più senza pretensione in opera - è questu hè a sequenza in quale sò custruiti. U rolu di l'analisi statica, chì, cum'è avemu capitu avà, hè capaci di sguassà solu i difetti più grossi, hè u rolu di a griglia "fanga" à u principiu di a cascata di filtru.

L'analisi statica per sè stessu ùn migliurà a qualità di u pruduttu finali, cum'è un "filtru di fangu" ùn rende micca l'acqua potabile. Eppuru, in cunghjunzione cù altri elementi di u pipeline, a so impurtanza hè evidenti. Ancu s'è in un filtru multistage, e fasi di output sò potenzialmente capaci di catturà tuttu ciò chì facenu e fasi di input, hè chjaru chì e cunsequenze risulterà da un tentativu di fà solu cù fasi di purificazione fine, senza tappe di input.

U scopu di a "trappula di fangu" hè di allevà e cascate successive da catturà difetti assai grossi. Per esempiu, à u minimu, a persona chì face a revisione di u codice ùn deve esse distractatu da un codice furmatu incorrectamente è violazioni di i normi di codificazione stabiliti (cum'è parentesi extra o rami troppu nidificati). Bugs cum'è NPE deve esse catturati da teste di unità, ma se ancu prima di a prova l'analizzatore ci indica chì un bug hè obligatu à succede, questu accelerà significativamente a so riparazione.

Credu chì avà hè chjaru perchè l'analisi statica ùn migliurà a qualità di u produttu s'ellu hè usatu in ocasioni, è deve esse usatu constantemente per filtrà i cambiamenti cù difetti grossi. A quistione di se l'utilizazione di un analizatore staticu migliurà a qualità di u vostru pruduttu hè quasi equivalenti à dumandà: "L'acqua presa da un stagnu bruttu serà migliurata in a qualità di beie si passa per un colatore?"

Implementazione in un prughjettu legatu

Una quistione pratica impurtante: cumu implementà l'analisi statica in u prucessu di integrazione cuntinuu cum'è "porta di qualità"? In u casu di teste automatiche, tuttu hè ovvi: ci hè un inseme di teste, u fallimentu di qualcunu d'elli hè abbastanza raghjone per crede chì l'assemblea ùn hà micca passatu a porta di qualità. Un tentativu di installà una porta in u listessu modu basatu annantu à i risultati di una analisi statica falla: ci sò troppu avvisi di analisi in u codice legatu, ùn vulete micca ignurà cumplitamenti, ma hè ancu impussibile di piantà di spedite un pruduttu. solu perchè cuntene avvisi di l'analizzatore.

Quandu s'utilice per a prima volta, l'analizzatore produce un gran numaru di avvisi nantu à qualsiasi prughjettu, a maiò parte di i quali ùn sò micca ligati à u funziunamentu propiu di u pruduttu. Hè impussibile di corriggerà tutti questi cumenti in una volta, è parechji ùn sò micca necessarii. Dopu tuttu, sapemu chì u nostru pruduttu in tuttu u travagliu, ancu prima di introduci l'analisi statica!

In u risultatu, assai sò limitati à l'usu occasionale di l'analisi statica, o l'utilizanu solu in modu d'infurmazione, quandu un rapportu di l'analizzatore hè solu emessu durante l'assemblea. Questu hè equivalente à l'absenza di ogni analisi, perchè s'ellu avemu digià parechje avvirtimenti, allora l'occurrence d'un altru (non importa quantu seriu) quandu cambia u codice passa inosservatu.

I seguenti metudi di introduzzione di porte di qualità sò cunnisciuti:

  • Stabbilisce un limitu à u numeru tutale di avvisi o u numeru di avvirtimenti divisu da u numeru di linee di codice. Questu travaglia pocu, perchè una tale porta permette liberamente i cambiamenti cù novi difetti per passà, sempre chì u so limitu ùn hè micca superatu.
  • Fixing, in un certu mumentu, tutti i vechji avvirtimenti in u codice cum'è ignurati, è ricusendu di custruisce quandu si verificanu novi avvisi. Sta funziunalità hè furnita da PVS-studio è qualchi risorse in linea, per esempiu, Codacy. Ùn aghju micca avutu l'uppurtunità di travaglià in PVS-studio, in quantu à a mo sperienza cù Codacy, u so prublema principali hè chì determinà ciò chì hè un "vechju" è ciò chì hè un errore "novu" hè un algoritmu piuttostu cumplessu chì ùn funziona micca sempre. currettamente, soprattuttu se i schedari sò assai mudificati o rinominati. In a mo spirimintà, Codacy puderia ignurà novi avvirtimenti in una dumanda di pull, mentre chì à u stessu tempu ùn passava micca una dumanda di pull per via di avvisi chì ùn eranu micca ligati à cambiamenti in u codice di un PR determinatu.
  • In u mo parè, a suluzione più efficace hè quella descritta in u libru Cunsigliu cuntinuu "metudu di criccatura". L'idea basica hè chì u numeru di avvisi di analisi statica hè una pruprietà di ogni liberazione, è solu i cambiamenti sò permessi chì ùn aumentanu micca u numeru tutale di avvirtimenti.

Ratchet

Funziona cusì:

  1. In u stadiu iniziale, un registru hè fattu in i metadata nantu à a liberazione di u numeru di avvirtimenti in u codice trovu da l'analizzatori. Allora, quandu custruite upstream, u vostru gestore di repository scrive micca solu "liberazione 7.0.2", ma "liberazione 7.0.2 chì cuntene 100500 avvisi di stile di cuntrollu". Se utilizate un gestore di repository avanzatu (cum'è Artifactory), almacenà tali metadati nantu à a vostra liberazione hè faciule.
  2. Avà ogni dumanda di pull, quandu custruita, paraguna u numeru di avvisi risultanti cù u numeru di avvisi dispunibuli in a versione attuale. Se PR porta à un aumentu di stu numeru, allura u codice ùn passa micca a porta di qualità per l'analisi statica. Se u numeru di avvirtimenti diminuite o ùn cambia micca, allora passa.
  3. À a prossima liberazione, u numeru ricalculatu di avvisi serà registratu novu in i metadati di liberazione.

Allora pocu à pocu ma fermamente (cum'è quandu un ratchet funziona), u numeru di avvirtimenti tende à cero. Di sicuru, u sistema pò esse ingannatu intruducendu un novu avvirtimentu, ma correggendu quellu di l'altru. Questu hè normale, perchè nantu à una longa distanza dà risultati: l'avvirtimenti sò curretti, per regula, micca individualmente, ma in un gruppu di un certu tipu à una volta, è tutti l'avvirtimenti facilmente removable sò eliminati abbastanza rapidamente.

Stu graficu mostra u numeru tutale di avvisi di Checkstyle per sei mesi di funziunamentu di un tali "ratchet". unu di i nostri prughjetti OpenSource. U numaru di avvirtimenti hè diminuitu da un ordine di grandezza, è questu hè accadutu naturali, in parallelu cù u sviluppu di u produttu!

Implementa l'analisi statica in u prucessu, piuttostu cà aduprà per truvà bug

Aduprà una versione mudificata di stu metudu, cuntendu separatamente l'avvertimenti per u modulu di u prughjettu è l'utile di analisi, risultatu in un schedariu YAML cù metadata di custruzzione chì pare cusì cusì:

celesta-sql:
  checkstyle: 434
  spotbugs: 45
celesta-core:
  checkstyle: 206
  spotbugs: 13
celesta-maven-plugin:
  checkstyle: 19
  spotbugs: 0
celesta-unit:
  checkstyle: 0
  spotbugs: 0

In ogni sistema di CI avanzatu, u ratchet pò esse implementatu per qualsiasi strumenti di analisi statica senza cunfidendu plugins è strumenti di terzu. Ogni analizzatore produce u so propiu rapportu in un testu simplice o furmatu XML chì hè faciule d'analizà. Tuttu ciò chì resta hè di scrive a logica necessaria in u script CI. Pudete vede cumu questu hè implementatu in i nostri prughjetti open source basatu in Jenkins è Artifactory ccà o ccà. I dui esempi dipendenu da a biblioteca ratchetlib: mètudu countWarnings() conta i tag xml in i schedarii generati da Checkstyle è Spotbugs in u modu di solitu, è compareWarningMaps() implementa u listessu ratchet, scacciendu un errore quandu u numeru di avvirtimenti in ogni categuria aumenta.

Una implementazione interessante di u "ratchet" hè pussibule per analizà l'ortografia di i cumenti, i testi literali è a documentazione cù aspell. Comu sapete, quandu verificate l'ortografia, micca tutte e parolle scunnisciute à u dizziunariu standard sò sbagliate; ponu esse aghjuntu à u dizziunariu di l'utilizatori. Se fate un dizziunariu persunalizatu parte di u codice fonte di u prugettu, allora a porta di qualità di l'ortografia pò esse formulata cusì: eseguisce aspell cù un dizziunariu standard è persunalizatu. Ùn ci vole micca ùn truvà micca sbagli di ortografia.

Circa l'impurtanza di riparà a versione di l'analizzatore

In cunclusioni, u puntu da nutà hè chì ùn importa micca cumu implementate l'analisi in u vostru pipeline di consegna, a versione di l'analizzatore deve esse fissata. Se permette à l'analizzatore di aghjurnà spontaneamente, allora, quandu si assemble a prossima dumanda di pull, i novi difetti ponu "apparisce" chì ùn sò micca ligati à i cambiamenti di codice, ma sò ligati à u fattu chì u novu analizzatore hè solu capace di truvà più difetti - è questu romperà u vostru prucessu di accettà e richieste di pull. L'aghjurnamentu di un analizatore deve esse una azione cuscente. Tuttavia, a fissazione rigida di a versione di ogni cumpunente di l'assemblea hè generalmente un requisitu necessariu è un tema per una discussione separata.

scuperti

  • L'analisi statica ùn truverete micca bug per voi è ùn migliurà a qualità di u vostru pruduttu per via di una sola applicazione. Un effettu pusitivu nantu à a qualità pò esse ottenutu solu per u so usu constante durante u prucessu di consegna.
  • Truvà bug ùn hè micca u compitu principalu di l'analisi in tuttu; a maiò parte di e funzioni utili sò dispunibuli in l'arnesi opensource.
  • Implementa porte di qualità basate nantu à i risultati di l'analisi statica in a prima tappa di u pipeline di consegna, utilizendu un "ratchet" per u codice legatu.

referenze

  1. Cunsigliu cuntinuu
  2. A. Kudryavtsev: Analisi di u prugramma: cumu capisce chì site un bonu programatore rapportu nantu à diversi metudi di analisi di codice (micca solu staticu!)

Source: www.habr.com

Add a comment