Rakendage protsessi staatilist analüüsi, mitte ei kasuta seda vigade leidmiseks

Seda artiklit ajendas mind kirjutama suur hulk staatilist analüüsi käsitlevaid materjale, mis on üha enam minu tähelepanu all. Esiteks see PVS-stuudio blogi, mis reklaamib end aktiivselt Habré lehel, analüüsides nende tööriista avatud lähtekoodiga projektides leitud vigu. Hiljuti võeti kasutusele PVS-stuudio Java tugi, ja loomulikult IntelliJ IDEA arendajad, kelle sisseehitatud analüsaator on Java jaoks tänapäeval ilmselt kõige arenenum, ei suutnud eemale hoida.

Selliseid arvustusi lugedes tekib tunne, et me räägime maagilisest eliksiirist: vajutage nuppu ja siin see on - teie silme ees on defektide loend. Tundub, et analüsaatorite täiustamisel leitakse automaatselt üha rohkem vigu ja nende robotite skannitud tooted muutuvad meiepoolse pingutuseta aina paremaks.

Kuid maagilisi eliksiire pole olemas. Tahaksin rääkida sellest, millest tavaliselt ei räägita postitustes nagu "siin on asjad, mida meie robot võib leida": mida analüsaatorid ei suuda, milline on nende tegelik roll ja koht tarkvara tarneprotsessis ning kuidas neid õigesti rakendada. .

Rakendage protsessi staatilist analüüsi, mitte ei kasuta seda vigade leidmiseks
Ratchet (allikas: Wikipedia).

Mida staatilised analüsaatorid kunagi ei suuda

Mis on lähtekoodi analüüs praktilisest vaatenurgast? Anname sisendiks lähtekoodi ja väljundina saame lühikese aja jooksul (palju lühema aja jooksul kui testid) teavet meie süsteemi kohta. Põhimõtteline ja matemaatiliselt ületamatu piirang seisneb selles, et sel viisil saame hankida vaid üsna kitsa klassi informatsiooni.

Kõige kuulsam näide probleemist, mida ei saa staatilise analüüsi abil lahendada, on väljalülitamise probleem: See on teoreem, mis tõestab, et on võimatu välja töötada üldist algoritmi, mis suudaks programmi lähtekoodi põhjal kindlaks teha, kas see teatud aja jooksul loopib või lõpetab. Selle teoreemi laiendus on Rice'i teoreem, mis väidab, et arvutatavate funktsioonide mis tahes mittetriviaalse omaduse puhul on algoritmiliselt lahendamatu probleem kindlaks teha, kas suvaline programm hindab sellise omadusega funktsiooni. Näiteks on võimatu kirjutada analüsaatorit, mis suudab mis tahes lähtekoodi põhjal kindlaks teha, kas analüüsitav programm on näiteks täisarvu ruudustamist arvutava algoritmi teostus.

Seega on staatiliste analüsaatorite funktsionaalsusel ületamatud piirangud. Staatiline analüsaator ei suuda kunagi kõigil juhtudel tuvastada selliseid asju nagu "null-osuti erandi" esinemine keeltes, mis lubavad nulli väärtust, või kõigil juhtudel määrata "null-osuti erandi" esinemine. atribuuti ei leitud" dünaamiliselt trükitud keeltes. Kõik, mida kõige arenenum staatiline analüsaator teha suudab, on erijuhtumite esiletõstmine, mille hulk kõigi lähtekoodiga seotud võimalike probleemide hulgas on liialdamata ämbris.

Staatiline analüüs ei seisne vigade leidmises

Eeltoodust järeldub järeldus: staatiline analüüs ei ole vahend programmis esinevate defektide arvu vähendamiseks. Julgen väita: kui see teie projektile esmakordselt rakendatakse, leiab see koodis "huvitavaid" kohti, kuid tõenäoliselt ei leia see ühtegi defekti, mis teie programmi kvaliteeti mõjutaks.

Näited analüsaatorite poolt automaatselt leitud defektidest on muljetavaldavad, kuid me ei tohiks unustada, et need näited leiti suure hulga suurte koodibaaside skaneerimisel. Samal põhimõttel leiavad häkkerid, kellel on võimalus proovida mitut lihtsat parooli suurel hulgal kontodel, lõpuks need kontod, millel on lihtne parool.

Kas see tähendab, et staatilist analüüsi ei tohiks kasutada? Muidugi mitte! Ja täpselt samal põhjusel, et iga uut parooli tasub kontrollida, veendumaks, et see on “lihtsate” paroolide loendis.

Staatiline analüüs on midagi enamat kui vigade leidmine

Tegelikult on analüüsiga praktiliselt lahendatavad probleemid palju laiemad. Lõppude lõpuks on staatiline analüüs üldiselt igasugune lähtekoodide kontrollimine, mis viiakse läbi enne nende käivitamist. Siin on mõned asjad, mida saate teha.

  • Kodeerimisstiili kontrollimine selle sõna kõige laiemas tähenduses. See hõlmab nii vormingu kontrollimist, tühjade/lisasulgude kasutamise otsimist, künnisväärtuste seadmist mõõdikutele, nagu ridade arv/meetodi tsüklomaatiline keerukus jne – kõike, mis võib takistada koodi loetavust ja hooldatavust. Javas on selline tööriist Checkstyle, Pythonis - flake8. Selle klassi programme nimetatakse tavaliselt "linteriteks".
  • Analüüsida saab mitte ainult käivitatavat koodi. Ressursifailide, nagu JSON, YAML, XML, .properties, kehtivust saab (ja tuleks!) automaatselt kontrollida. Lõppude lõpuks on parem teada saada, et JSON-struktuur on mõne sidumata hinnapakkumise tõttu katki automaatse tõmbetaotluse kontrollimise varases staadiumis, kui testi täitmise või käitamisaja ajal? Saadaval on vastavad vahendid: nt. YAMLlint, JSONLint.
  • Kompileerimine (või dünaamiliste programmeerimiskeelte jaoks sõelumine) on samuti teatud tüüpi staatiline analüüs. Üldiselt on kompilaatorid võimelised andma hoiatusi, mis viitavad lähtekoodi kvaliteediprobleemidele ja mida ei tohiks ignoreerida.
  • Mõnikord on kompileerimine midagi enamat kui lihtsalt käivitatava koodi koostamine. Näiteks kui teil on vormingus dokumentatsioon AsciiDoctor, siis selle HTML-iks/PDF-iks muutmise hetkel AsciiDoctori töötleja (Maven pistikprogramm) võib anda hoiatusi näiteks katkiste siselinkide kohta. Ja see on hea põhjus mitte vastu võtta Tõmbetaotlust koos dokumentatsioonimuudatustega.
  • Õigekirjakontroll on ka staatilise analüüsi tüüp. Kasulikkus loits suudab kontrollida õigekirja mitte ainult dokumentatsioonis, vaid ka programmi lähtekoodides (kommentaarid ja literaalid) erinevates programmeerimiskeeltes, sh C/C++, Java ja Python. Defektiks on ka õigekirjaviga kasutajaliideses või dokumentatsioonis!
  • Konfiguratsioonitestid (mis need on - vt. see и see aruanded), kuigi need käivitatakse ühikutesti käitusajal, näiteks pytest, on tegelikult ka teatud tüüpi staatiline analüüs, kuna need ei käivita nende käivitamise ajal lähtekoode.

Nagu näete, mängib selles loendis olevate vigade otsimine kõige vähem olulist rolli ja kõik muu on saadaval tasuta avatud lähtekoodiga tööriistu kasutades.

Millist neist staatilise analüüsi tüüpidest peaksite oma projektis kasutama? Muidugi, mida rohkem, seda parem! Peaasi on seda õigesti rakendada, mida arutatakse edasi.

Tarnetoru kui mitmeastmeline filter ja staatiline analüüs selle esimese etapina

Pideva integratsiooni klassikaline metafoor on konveier, mille kaudu liiguvad muutused, alates lähtekoodi muudatustest kuni tarnimiseni tootmiseni. Selle torujuhtme standardne etappide jada näeb välja järgmine:

  1. staatiline analüüs
  2. koostamine
  3. ühikutestid
  4. integratsioonitestid
  5. UI testid
  6. käsitsi kontroll

Gaasijuhtme N etapis tagasi lükatud muudatusi ei kanta üle etappi N+1.

Miks just nii ja mitte teisiti? Torujuhtme testimise osas tunnevad testijad ära tuntud testimispüramiidi.

Rakendage protsessi staatilist analüüsi, mitte ei kasuta seda vigade leidmiseks
Testpüramiid. Allikas: artikkel Martin Fowler.

Selle püramiidi allosas on testid, mida on lihtsam kirjutada, mida on kiirem täita ja millel ei ole kalduvust ebaõnnestuda. Seetõttu peaks neid olema rohkem, need peaksid katma rohkem koodi ja olema kõigepealt käivitatud. Püramiidi tipus on vastupidi, seega tuleks integratsiooni- ja kasutajaliidese testide arvu vähendada vajaliku miinimumini. Selle ahela inimene on kõige kallim, aeglasem ja ebausaldusväärsem ressurss, seega on ta päris lõpus ja teeb tööd ainult siis, kui eelmised etapid ei leidnud ühtegi defekti. Samas kasutatakse samu põhimõtteid torujuhtme ehitamisel osades, mis ei ole otseselt testimisega seotud!

Tahaksin pakkuda analoogi mitmeastmelise vee filtreerimissüsteemi näol. Sisendisse juhitakse must vesi (muutused koos defektidega), väljundis peame saama puhast vett, milles kõik soovimatud saasteained on kõrvaldatud.

Rakendage protsessi staatilist analüüsi, mitte ei kasuta seda vigade leidmiseks
Mitmeastmeline filter. Allikas: Wikimedia Commons

Teatavasti on puhastusfiltrid konstrueeritud nii, et iga järgnev kaskaad suudab välja filtreerida üha peenemat osa saasteaineid. Samal ajal on jämedamatel puhastuskaskaadidel suurem läbilaskevõime ja madalam hind. Meie analoogia kohaselt tähendab see, et sisendkvaliteediga väravad on kiiremad, nõuavad vähem pingutust käivitamiseks ja on töös tagasihoidlikumad – ja see on nende ehitamise järjekord. Staatilise analüüsi roll, mis, nagu me nüüd mõistame, suudab välja rookida ainult kõige jämedamaid defekte, on filtrikaskaadi alguses oleva "muda" ruudustiku roll.

Staatiline analüüs iseenesest ei paranda lõpptoote kvaliteeti, nii nagu “mudafilter” ei muuda vett joogikõlbulikuks. Ja siiski, koos teiste torujuhtme elementidega, on selle tähtsus ilmne. Kuigi mitmeastmelises filtris on väljundastmed potentsiaalselt võimelised jäädvustama kõike, mida sisendastmed teevad, on selge, millised tagajärjed kaasnevad katsega hakkama saada ainult peenpuhastuse etappidega, ilma sisendastmeteta.

“Mudalõksu” eesmärk on vabastada järgnevad kaskaadid väga jämedate defektide tabamisest. Näiteks ei tohiks koodiülevaatust tegevat inimest häirida vähemalt valesti vormindatud kood ja kehtestatud kodeerimisstandardite rikkumine (nt lisasulud või liiga sügavalt pesastatud harud). Vead, nagu NPE-d, tuleks ühikutestidega kinni püüda, kuid kui analüsaator annab meile juba enne testi märku, et viga kindlasti juhtub, kiirendab see oluliselt selle parandamist.

Usun, et nüüd on selge, miks staatiline analüüs ei paranda toote kvaliteeti, kui seda aeg-ajalt kasutada, ja seda tuleks pidevalt kasutada tõsiste defektidega muutuste väljafiltreerimiseks. Küsimus, kas staatilise analüsaatori kasutamine parandab teie toote kvaliteeti, on ligikaudu samaväärne küsimusega: "Kas määrdunud tiigist võetud vee joogikvaliteet paraneb, kui see juhitakse läbi kurni?"

Pärandprojekti juurutamine

Oluline praktiline küsimus: kuidas rakendada staatilist analüüsi pidevasse integratsiooniprotsessi kui “kvaliteediväravat”? Automaattestide puhul on kõik ilmselge: testide komplekt on olemas, ühegi neist ebaõnnestumine on piisav põhjus arvata, et koost ei läbinud kvaliteediväravat. Katse paigaldada värav samamoodi staatilise analüüsi tulemuste põhjal ebaõnnestub: pärandkoodis on liiga palju analüüsihoiatusi, neid ei taha täielikult ignoreerida, kuid toote tarnimist on ka võimatu peatada lihtsalt sellepärast, et see sisaldab analüsaatori hoiatusi.

Esmakordsel kasutamisel annab analüsaator iga projekti kohta tohutul hulgal hoiatusi, millest valdav enamus ei ole seotud toote nõuetekohase toimimisega. Kõiki neid kommentaare korraga parandada on võimatu ja paljud pole ka vajalikud. Lõppude lõpuks teame, et meie toode tervikuna töötab, isegi enne staatilise analüüsi kasutuselevõttu!

Seetõttu piirduvad paljud staatilise analüüsi aeg-ajalt kasutamisega või kasutavad seda ainult teaberežiimis, kui analüsaatori aruanne koostamise ajal lihtsalt väljastatakse. See võrdub igasuguse analüüsi puudumisega, sest kui meil on juba palju hoiatusi, siis teise (ükskõik kui tõsise) esinemine koodi muutmisel jääb märkamatuks.

Kvaliteetsete väravate kasutuselevõtuks on teada järgmised meetodid:

  • Hoiatuste koguarvu limiidi määramine või hoiatuste arvu jagamine koodiridade arvuga. See toimib halvasti, sest selline värav laseb vabalt läbi uute defektidega muudatused, kui nende limiiti ei ületata.
  • Kinnitatakse teatud hetkel kõik koodis olevad vanad hoiatused ignoreerituks ja uute hoiatuste ilmnemisel keeldutakse koostamisest. Seda funktsiooni pakuvad PVS-studio ja mõned võrguressursid, näiteks Codacy. Mul ei olnud võimalust PVS-stuudios töötada, kuna minu kogemus Codacyga on nende peamine probleem see, et tuvastada, mis on "vana" ja mis "uus" viga, on üsna keeruline algoritm, mis alati ei tööta. õigesti, eriti kui faile on palju muudetud või ümber nimetatud. Minu kogemuse kohaselt võis Codacy eirata tõmbepäringu uusi hoiatusi, jättes samal ajal tõmbamistaotluse läbimata hoiatuste tõttu, mis ei olnud seotud antud PR koodi muudatustega.
  • Minu arvates on kõige tõhusam lahendus raamatus kirjeldatud Pidev kohaletoimetamine "põrkimismeetod". Põhiidee seisneb selles, et staatilise analüüsi hoiatuste arv on iga väljalaske omadus ja lubatud on ainult sellised muudatused, mis hoiatuste koguarvu ei suurenda.

Ratchet

See toimib järgmiselt:

  1. Algstaadiumis tehakse metaandmetesse kirje analüsaatorite leitud koodis olevate hoiatuste arvu vabastamise kohta. Seega, kui loote ülesvoolu, ei kirjuta teie hoidla haldur mitte ainult "väljalase 7.0.2", vaid "väljalase 7.0.2, mis sisaldab 100500 XNUMX kontrollstiili hoiatust." Kui kasutate täiustatud hoidlahaldurit (nt Artifactory), on selliste metaandmete salvestamine oma versiooni kohta lihtne.
  2. Nüüd võrdleb iga tõmbepäring, kui see on koostatud, saadud hoiatuste arvu praeguses versioonis saadaolevate hoiatuste arvuga. Kui PR toob kaasa selle arvu suurenemise, siis kood ei läbi staatilise analüüsi kvaliteediväravat. Kui hoiatuste arv väheneb või ei muutu, siis see möödub.
  3. Järgmisel väljalaskmisel salvestatakse ümberarvutatud hoiatuste arv uuesti versiooni metaandmetesse.

Nii vähehaaval, kuid pidevalt (nagu põrkmehhanismi töötamise ajal) kipub hoiatuste arv nulli minema. Muidugi võib süsteemi petta uue hoiatuse sisseviimisega, aga kellegi teise oma parandamisega. See on normaalne, sest pikemal distantsil annab see tulemusi: hoiatusi parandatakse reeglina mitte üksikult, vaid teatud tüüpi grupis korraga ja kõik kergesti eemaldatavad hoiatused kõrvaldatakse üsna kiiresti.

See graafik näitab Checkstyle'i hoiatuste koguarvu sellise põrkmehhanismi kuue kuu jooksul üks meie avatud lähtekoodiga projektidest. Hoiatuste arv on suurusjärgu võrra vähenenud ja see juhtus loomulikult, paralleelselt tootearendusega!

Rakendage protsessi staatilist analüüsi, mitte ei kasuta seda vigade leidmiseks

Kasutan selle meetodi muudetud versiooni, loendan hoiatused eraldi projektimooduli ja analüüsitööriista kaupa, mille tulemuseks on YAML-fail koos ehituse metaandmetega, mis näeb välja umbes selline:

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

Igas täiustatud CI-süsteemis saab põrkmehhanismi rakendada mis tahes staatilise analüüsi tööriistade jaoks ilma pistikprogrammidele ja kolmandate osapoolte tööriistadele tuginemata. Iga analüsaator koostab oma aruande lihtsas teksti- või XML-vormingus, mida on lihtne analüüsida. Jääb vaid vajalik loogika CI skripti sisse kirjutada. Näete, kuidas seda rakendatakse meie avatud lähtekoodiga projektides, mis põhinevad Jenkinsil ja Artifactoryl siin või siin. Mõlemad näited sõltuvad raamatukogust ratchetlib: meetod countWarnings() loendab Checkstyle'i ja Spotbugs'i loodud failides tavapärasel viisil xml silte ja compareWarningMaps() rakendab sama põrkmehhanismi, andes vea, kui mõne kategooria hoiatuste arv suureneb.

"Põrketi" huvitav teostus on võimalik kommentaaride, tekstiliteraalide ja dokumentatsiooni õigekirja analüüsimiseks aspelliga. Nagu teate, ei ole õigekirja kontrollimisel kõik tavasõnastikule tundmatud sõnad valed, neid saab lisada kasutaja sõnastikku. Kui muudate kohandatud sõnastiku projekti lähtekoodi osaks, saab õigekirjakvaliteedi värava sõnastada järgmiselt: käivitage aspell standardse ja kohandatud sõnastikuga ei peaks ei leia kirjavigu.

Analüsaatori versiooni parandamise tähtsusest

Kokkuvõtteks tuleb märkida, et olenemata sellest, kuidas te analüüsi oma tarnetorustikus rakendate, tuleb analüsaatori versioon fikseerida. Kui lubate analüsaatoril spontaanselt värskendada, siis järgmise tõmbepäringu kokkupanemisel võivad ilmneda uued vead, mis pole seotud koodimuutustega, vaid on seotud sellega, et uus analüsaator suudab lihtsalt rohkem defekte leida - ja see katkestab teie tõmbamistaotluste vastuvõtmise protsessi. Analüsaatori uuendamine peaks olema teadlik tegevus. Siiski on iga koostekomponendi versiooni jäik fikseerimine üldiselt vajalik nõue ja eraldi arutelu teema.

Järeldused

  • Staatiline analüüs ei leia teie jaoks vigu ega paranda teie toote kvaliteeti ühe rakenduse tulemusena. Positiivset mõju kvaliteedile saab saavutada ainult selle pideva kasutamisega tarneprotsessi ajal.
  • Vigade leidmine pole üldsegi analüüsi põhiülesanne, valdav enamus kasulikest funktsioonidest on saadaval avatud lähtekoodiga tööriistades.
  • Rakendage staatilise analüüsi tulemustel põhinevad kvaliteediväravad tarnetorustiku kõige esimeses etapis, kasutades pärandkoodi jaoks „põrkmehhanismi“.

Viited

  1. Pidev kohaletoimetamine
  2. A. Kudrjavtsev: Programmianalüüs: kuidas aru saada, et oled hea programmeerija aruanne erinevate koodianalüüsi meetodite kohta (mitte ainult staatiline!)

Allikas: www.habr.com

Lisa kommentaar