Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu

Seda artikkel kirjutama on mind inspireerinud suur hulk materjale staatilisest analüüsist, mis järjest enam silma hakkavad. Esiteks, see on PVS-studio blogi, mis reklaamib end aktiivselt Habr's vigade ülevaadete kaudu, mille nende tööriist on leidnud avatud lähtekoodiga projektidest. Hiljuti rakendas PVS-studio Java toe, ja loomulikult ei saanud IntelliJ IDEA arendajad, kelle sisseehitatud analüsaator on ilmselt kõige arenenum Java jaoks, jääda kõrvale Kui selliseid ülevaateid lugeda, tekib tunne, et tegemist on maagilise eliksiiriga: vajutad nuppu ja voilà — defektide nimekiri on silme ees. Tundub, et analüsaatorite täiustamisega hakkavad vead automaatselt üha rohkem esinema, ja tooted, mida need robotid skaneerivad, muutuvad üha paremaks, meie enda poolsete pingutusteta..

Kuid maagilisi eliksiire ei ole. Soovin rääkida asjadest, millest tavaliselt ei räägita postitustes „mida meie robot suudab leida”: mida analüsaatorid kunagi ei saa, milline on nende tõeline roll ja koht tarkvara tarnimise protsessis ning kuidas neid õigesti kasutada.

Ratta hambad (allikas:

Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu
vikipeedia )).

Mida staatilised analüsaatorid kunagi ei suuda

Mis on, praktilisest vaatepunktist, lähtekoodi analüüs? Me anname sisse mõningad lähtekoodid ja saame väljundina lühikese aja jooksul (kui palju kiiremini kui testide läbimine) teavet meie süsteemi kohta. Põhimõtteline ja matemaatiliselt ületamatu piirang seisneb selles, et saame sel viisil teada ainult üsna kitsast teabe klassist.

Kõige tuntum näide probleemist, mida ei saa lahendada staatilise analüüsi abil — peatumise probleem: see on teoreem, mis tõestab, et ei ole võimalik välja töötada üldist algoritmi, mis saaks lähtekodi põhjal määrata, kas programm jääb tsüklisse või lõpeb lõpliku aja jooksul. Selle teoreemi laienduseks on Reysi teoreem, mis väidab, et iga mitte-triviaalse arvutifunktsiooni omaduse määramine, kas mingi programm arvutab sellise omadusega funktsiooni, on algoritmiliselt lahendamatu ülesanne. Näiteks ei ole võimalik kirjutada analüsaatorit, mis igasuguse lähtekoodi põhjal määraks, kas analüüsitav programm on algoritmi rakendamine, mis arvutab, ütleme, täisarvu ruutu.

Seega on staatiliste analüsaatorite funktsionaalsusel ületamatud piirangud. Staatiline analüsaator ei suuda kunagi kõigis juhtudel määrata selliseid asju nagu näiteks nullviidatud erandi tekkimine keeltes, mis lubavad nullväärtust, või alati määrata, kas dünaamilise tüübistamisega keeltes tekib „atribuut ei leitud”. Kõik, mida kõige arenenum staatiline analüsaator suudab teha, on tuvastada erijuhte, mille hulk kõikide võimalike probleemide seas teie lähtekoodis on liialdamata tilk meres.

Staatiline analüüs ei ole veade otsimine

Käimasolevast järjekorrast tuleneb järeldus: staatiline analüüs ei ole vahend defektide arvu vähendamiseks programmis. Julgen väita, et kui seda esmakordselt teie projekti rakendada, leiab see koodist „huvitavaid” kohti, kuid tõenäoliselt ei leia see ühtegi defekti, mis mõjutaks teie programmi töö kvaliteeti.

Automaatsete analüsaatorite leidnud defektide näited on muljetavaldavad, kuid ärge unustage, et need näited on leitud suure koodibaasi skaneerimise käigus. Samamoodi võivad häkkerid, kellel on võimalus katsetada mitmeid lihtsaid paroole paljude kontodega, viimaks leida kontosid, kus on lihtne parool.

Kas see tähendab, et staatilist analüüsi ei tohiks kasutada? Loomulikult mitte! Ja just selle pärast, miks on mõistlik kontrollida iga uut parooli „lihtsate” paroolide mustris.

Staatiline analüüs on rohkem kui veade otsimine

Tegelikult on tõeliselt lahendatavad analüüsi ülesanded oluliselt laiemad. Lõppude lõpuks on staatiline analüüs igasugune lähtekoodide kontroll, mis toimub enne nende käivitamist. Siin on mõned asjad, mida saab teha:

  • Koodimise stiili kontroll laiemas mõttes hõlmab nii vormindamise kontrollimist kui ka tühjade/ülejäänud sulgude kasutamise leidmist, mõõdikute nagu koodiridade arvu/cyclomatic complexity sätteid ja piirväärtusi jne — kõike, mis potentsiaalselt raskendab koodi loetavust ja hooldust. Java-s on selline tööriist Checkstyle, Pythonis aga flake8. Selliseid programme nimetatakse sageli "linteerijateks".
  • Analüüsi all võib olla mitte ainult käivitatav kood. Ressursifailid, nagu JSON, YAML, XML, .properties, võivad (ja peavad!) olema automaatselt kontrollitud kehtivuse osas. Lõppude lõpuks on parem teada, et mõne paarita jutumärgi tõttu on JSON-i struktuur rikutud varajases Pull Requesti automaatkontrolli etapis, kui siis, kui testid või Run time seda proovivad? Vastavad tööriistad on olemas: näiteks YAMLlint, JSONLint.
  • Kompilatsioon (või dünaamiliste programmeerimiskeelte puhul parsimine) on samuti staatilise analüüsi tüüp. Reeglina suudavad kompilaatorid anda hoiatusi, mis viitavad allika kvaliteedi probleemidele, ja neid hoiatusi ei tohiks eirata.
  • Mõnikord ei ole kompilatsioon ainult käivitatava koodi kompilatsioon. Näiteks kui sul on dokumentatsioon formaadis AsciiDoctor, siis selle muutmisel HTML-iks/PDF-iks võib AsciiDoctor (Maven plugin) anda hoiatusi, näiteks sisemiste linkide rikkumise kohta. Ja see on olulise põhjus, et mitte aktsepteerida Pull Requesti dokumentatsiooni muudatustega.
  • Õigekirjakontroll on samuti staatilise analüüsi tüüp. Tööriist aspell suudab kontrollida õigekirja mitte ainult dokumentatsioonis, vaid ka programmikoodides (kommentaarides ja literalides) erinevates programmeerimiskeeltes, sealhulgas C/C++, Java ja Python. Õigekirja viga kasutajaliideses või dokumentatsioonis on samuti defekt!
  • Konfiguratsioonitestid (millest rääkida — vt. seda ja seda ettekanded), kuigi neid täidetakse pytesti sarnastes keskkondades, on tegelikult samuti staatilise analüüsi tüüp, kuna nad ei käivita allika koodi oma täitmise käigus.

Nagu näeme, mängib veaparandamine selle nimekirja kontekstis kõige vähem tähtsat rolli, aga kõik muu on kergesti kättevõtte tasuta open source tööriistade abil kergesti kätte saadav.

Milliseid nendest staatilise analüüsi tüpidest tuleks kasutada teie projektis? Loomulikult kõiki, mida rohkem - seda parem! Peamine on õigesti rakendada, millest järgmises osas räägitakse.

Tarneprotsess kui mitmeastmeline filter ja staatiline analüüs kui selle esialgne kaskaad

Traditsiooniline metafoor pidevaks integreerimiseks on pipeline, mille kaudu voolavad muudatused - allika koodist toimetamiseni tootmisse. Selle pipelini tavaline etapp on järgmine:

  1. staatiline analüüs
  2. kompilatsioon
  3. moodulitestid
  4. integreerimistestid
  5. UI testid
  6. kätega kontrollimine

Muutused, mis lükatakse tagasi N. etapil, ei lähe edasi etapile N+1.

Miks just nii, mitte teisiti? Testimisega seotud etapis saavad testerid teada laialt tuntud testimise püramiidist.

Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu
Testimise püramiid. Allikas: artikkel Martin Fowler.

Püramiidi alumises osas asuvad testid, mida on lihtsam kirjutada, mis täidetakse kiiremini ja millel ei ole valehäirete kalduvust. Seetõttu peaks neid olema rohkem, nad peaksid katma rohkem koodi ja nad täidetakse esimesena. Püramiidi ülemises osas on vastupidi, seetõttu tuleks integreerimis- ja UI-testide arvu vähendada vajalikule miinimumile. Inimene selles ahelas on kõige kallim, aeglasem ja usaldamatum ressurss, seetõttu on ta kõige lõpus ja täidab tööd ainult siis, kui eelnevad etapid ei ole mingeid defekte leidnud. Kuid sama põhimõtte järgi üles ehitatud ka pipeline osades, mis ei ole otseselt seotud testimisega!

Sooviksin pakkuda analoogiat mitmeastmelise vee filtreerimisse süsteemiga. Sissevoolu näidatakse musta veena (defektidega muudatused), väljundil peame saama puhta vee, kõik soovimatud saasteained on eemaldatud.

Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu
Mitmeastmeline filter. Allikas: Wikimedia Commons

Nagu teada, puhastavad filtrid on kujundatud nii, et iga järgmine kaskad suudab filtreerida üha väiksemaid saastefraktsioone. Sellega seoses on rohusõelumise kaskadidel suurem läbilaskevõime ja madalam hind. Meie analoogias tähendab see, et sisendquality gates on suurema jõudluse ja nende käivitamine nõuab vähem pingutust, nad on ka enesestmõistetavad tööshoidmiselt — ning just seda järjekorda nad järgivad. Staatilise analüüsi roll, mis, nagu me nüüd mõistame, suudab tuvastada vaid kõige paksemaid vigu — on võrgusilma roll kaskadi alguses.

Staatiline analüüs iseenesest ei paranda lõpp-produkti kvaliteeti, nagu võrgusilm ei muuda vett joodavaks. Siiski, koos teiste komponentidega, on selle olulisus ilmne. Kuigi mitmikfiltris on väljundkaskadid potentsiaalselt võimelised püüdma sama, mis sisendkaskadid — on selge, millistele tagajärgedele võib viia katse pideva peenetootmise kaskadega, ilma sisendkaskadideta.

Võrgusila eesmärk on vabastada järgmised kaskadid kõige paksemete vigade püüdmisest. Näiteks ei tohiks vähemalt koodiarvutaja segada end valesti vormindatud koodi ja kehtestatud koodinormide rikkumisega (nagu liialdatud sulud või liiga sügavad harunemised). Vead nagu NPE peaksid olema püütud moodulitestide kaudu, kuid kui juba enne testi analüsaator meid hoiatab, et viga on paratamatu — kiirendab see oluliselt selle parandamist.

Ma arvan, et nüüd on selge, miks staatiline analüüs ei paranda toote kvaliteeti, kui seda rakendatakse episoodselt, ja peab olema pidev, et kõrvaldada muudatusi, mis sisaldavad suuri vigu. Küsimus, kas staatilise analüsaatori kasutamine parandab teie toote kvaliteeti, on umbes sama, mis küsida: "kas vees, mis on saadud mudasest veekogust, paranevad joodavad omadused, kui seda lasta läbi sõela?"

Implementatsioon legacy-projekti

Oluline praktiline küsimus: kuidas juurutada staatiline analüüs pideva integreerimise protsessis kui "quality gate"? Automaatsete testide puhul on kõik selge: on katsete komplekt, mille ebaõnnestumine on piisav põhjus, et arvata, et kogumine ei läbinud quality gate'i. Üritus kehtestada sama moodi gate staatilise analüüsi tulemuste põhjal nurjub: legacy-koodil on analüüsi hoiatusi liiga palju, neid ei saa täielikult ignoreerida, kuid toote tarnimine ei saa peatuda ainult selle pärast, et selles on analüsaatori hoiatused.

Esmakordselt rakendades annab analüsaator igasuguste hõikude hulga, millest enamik ei ole seotud toote töökindlusega. Kõiki need märkusi kohe parandada ei saa, paljusid — ja ei olegi vaja. Lõppude lõpuks teame, et meie toode töötab üldiselt ja enne staatilise analüüsi rakendamist!

Lõpuks piirdub paljud staatilise analüüsi episoodilise kasutamisega või kasutavad seda ainult teavitamise režiimis, kui koostamise ajal antakse lihtsalt analüsaatori aruande. See on võrreldav igasuguse analüüsi puudumisega, kuna kui meil on juba palju hoiatusi, siis uue (kui tõsise) puudumine koodi muutmisel jääb tähelepanuta.

On teada järgmised meetodid quality gates'i kehtestamiseks:

  • Üldiste hoiatusete arvu või hoiatusete arvu koodi ridade arvu järgi kehtestamine. See ei toimi hästi, sest selline gate lubab vabalt muudatusi uute vigadega, kuni nende limiit ületatakse.
  • Kõik vana hoiatused kodeerimise hetkel fikseeritakse kui ignoreeritavad, ja uute hoiatuste korral arvutada pole. Sellist funktsionaalsust pakuvad PVS-stuudio ja mõned veebiteenused, näiteks Codacy. Ma ei ole PVS-stuudioga töötanud, kuid minu kogemus Codacyga on see, et määratleda, mis on "vana" ja mis on "uus" viga on üsna keeruline ja mitte alati täpselt töötav algoritm, eriti kui failid muutuvad või nimetatakse ümber. Minu mäletamist mööda jõudis Codacy lubama pulbrekvisitsiooni uute hoiatuste, samas kui ei lasknud pull request'i läbi hoiatuste tõttu, mis ei olnud seotud koodi selle PR-i muudatustega.
  • Minu arvates on kõige tõhusam lahendus see, mis on kirjeldatud raamatus Jätkuv kohaletoimetamine «rattimismeetod» («ratcheting»). Peamine idee seisneb selles, et iga versiooni omadus on staatilise analüüsi hoiatuste arv ning lubatud on vaid sellised muudatused, mis ei suurenda hoiatuste koguarvu.

Rattimeetod

See toimib järgmiselt:

  1. Esialgses etapis salvestatakse versiooni metainfos hoiatuste arv koodis, mille analüsaatorid on leidnud. Nii salvestatakse peamise haru kompileerimisel teie repotejuriis mitte lihtsalt «versioon 7.0.2», vaid «versioon 7.0.2, mis sisaldab 100500 Checkstyle-hoiatust». Kui kasutate arenenud repotejuriisi (nt Artifactory), on selliste metainfode säilitamine teie versiooni jaoks lihtne.
  2. Nüüd võrdleb iga pull request kompileerimisel saadud hoiatuste arvu selle arvu kanssa, mis on praegusel versioonil. Kui PR toob kaasa selle arvu suurenemise, siis ei läbinud kood kvaliteedi väravat staatilise analüüsi osas. Kui hoiatuste arv väheneb või püsib samana, siis läbib see.
  3. Järgmise versiooni puhul salvestatakse uuesti arvutatud hoiatuste arv versiooni metainfosse.

Nii püsivalt, kuid kindlalt (nagu rattimeetod töötab) püüab hoiatuste arv jõuda nullini. Loomulikult saab süsteemi petta, lisades uue hoiatuse, kuid parandades kellegi teise oma. See on normaalne, kuna pikaajaliselt annab see tulemuse: hoiatuste parandamine ei käi tavaliselt ükshaaval, vaid kohe rühmades, mis on teatud tüüpi, ja kõik lihtsalt kõrvaldatavad hoiatuste hulk kõrvaldatakse üsna kiiresti.

Sellel graafikul on näidatud Checkstyle-hoiatuste koguarv kuue kuu jooksul, kui selline «rattimeetod» töötas ühe meie OpenSource projektide juures. Hoiatusi on vähendatud korralikult, ja see juhtus loomulikult, paralleelselt toote arendamisega!

Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu

Kasutame muudetud versiooni sellest meetodist, arvutades hoiatuste arvu projektimoodulite ja analüüsi tööriistade kaupa. Sellest tulenev YAML-fail versiooni metainfode kohta 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 arenenud CI-süsteemis on «rattimeetod» võimalik rakendada iga staatilise analüüsi tööriista jaoks, tuginedes mitte pluginatele ja kolmandate osapoolte tööriistadele. Iga analüsaator annab oma aruande lihtsas tekstivormingus või XML formaadis, mis on hõlpsasti analüüsitav. Alles jääb vaid vajalik loogika CI-skriptis üles kirjutada. Kuidas seda on teostatud meie open source projektides Jenkins'i ja Artifactory baasil, saab vaadata siit või siit. Mõlemad näited sõltuvad raamatukogust ratchetlib: meetod countWarnings() loendab tavaliselt xml-märgiseid failides, mille on loonud Checkstyle ja Spotbugs, ja compareWarningMaps() rakendab seda rattimeetodit, andes vea, kui hoiatuste arv mõnes kategoorias tõuseb.

Huvitav variatsioon «rattimeetodi» rakendamiseks on kommentaaride, tekstiliste literatuuride ja dokumentatsiooni õigekirja analüüsimiseks aspelli abil. Nagu teada, ei ole kõik teadmata sõnad, mis ei ole standardsetes sõnastikes, vale, nad võivad olla lisatud kasutaja sõnastikku. Kui teha kasutaja sõnastik projekti lähtekoodi osaks, võib õigekirja kvaliteedi värav olla formuleeritud järgmiselt: aspelli käivitamine standardse ja kasutaja sõnastikuga ei tohi leida mingeid õigekirjavigu.

Analüsaatori versiooni fikseerimise tähtsus

Kokkuvõtteks on oluline mainida järgmist: kuidas iganes te analüüsi oma tarnimisprotsessi rakendate, peab analüsaatori versioon olema fikseeritud. Kui lubate analüsaatori spontaanse uuendamise, võivad järgmise pull requesti koostamisel ilmuda uued defektid, mis ei ole seotud koodimuudatustega, vaid sellega, et uus analüsaator suudab lihtsalt leida rohkem defekte - ja see rikub teie pull requestide aktsepteerimise protsessi. Analüsaatori uuendamine peab olema teadlik tegevus. Siiski, iga koostisosade versiooni range fikseerimine on üldiselt vajalik nõue ja eraldi teema.

Järeldused

  • Staatiline analüüs ei leia teile vigu ega paranda teie toote kvaliteeti üheainsa rakendusega. Positiivne efekt kvaliteedis tuleb ainult siis, kui seda rakendatakse pidevalt tarnimisprotsessi käigus.
  • Vigade otsimine ei ole analüüsi peamine ülesanne, enamik kasulikest funktsioonidest on saadaval avatud lähtekoodiga tööriistades.
  • Rakendage kvaliteedi kontrolle staatilise analüüsi tulemustel, kasutades 'ratast' pärandkoodi puhul tarnimise konveieril juba algfaasis.

Lingid

  1. Jätkuv kohaletoimetamine
  2. A. Kudrjavtsev: Programmide analüüs: kuidas mõista, et oled hea programmeerija ettekanne erinevatest koodi analüüsi meetoditest (mitte ainult staatilisest!)

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster