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

Selle artikli kirjutamiseks inspiratsiooni andis mulle suur hulk statilise analüüsi materjale, mis silma hakkasid. Esiteks, see on PVS-studio blogi, mis aktiivselt edendab end Habras, osaledes nende tööriista avatud lähtekoodiga projektides leiduvate vigade ülevaadetes. Hiljuti on PVS-studio realiseerinud Java toe, ja loomulikult ei saanud IntelliJ IDEA arendajad, kelle sisseehitatud analüsaator on täna tõenäoliselt kõige arenenum Java jaoks, ümber jääda.

Selliste ülevaadete lugemisel tekib tunne, et tegu on maagilise eliksiiriga: vajuta nuppu ja voilà — defektide nimekiri on su silme ees. Tundub, et analüsaatorite täiustamisel tuvastatakse automaatselt üha rohkem ja rohkem vigu ning neid roboteid skaneeritud tooted muutuvad meiepoolse vaevata üha paremaks.

Aga maagilisi eliksiire ei ole olemas. Soovin rääkida sellest, millest tavaliselt ei räägita postitustes, mis ütlevad "siin on, milliseid asju võib meie robot leida": mida analüsaatorid ei suuda teha, milline on nende tõeline roll ja koht tarkvara tarnimise protsessis ning kuidas neid õigesti rakendada.

Rakendage staatiline analüüs protsessi, mitte ärge otsige selle abil vigu
Hammasratas (allikas: Wikipeedia).

) Mida statilised analüsaatorid kunagi ei suuda

Mis on, praktiliselt vaadates, lähtekoodi analüüs? Me esitame teatud lähtekoodid ning saame lühikese aja jooksul (kõvasti lühem aeg kui testide käitamine) mõned ülevaated meie süsteemi kohta. Põhimõtteline ja matemaatiliselt ületamatu piirang on see, et me saame sel viisil vaid üsna kitsast teabe klassi.

Kuulsaim näide probleemist, mida ei saa lahendada staatilise analüüsi abil on peatuse probleem: see on teoreem, mis tõestab, et ei ole võimalik välja töötada üldist algoritmi, mis määraks programmeri lähtekoodi põhjal, kas see satub lõksu või lõpetab mõistliku aja jooksul. Selle teoreemi laiendus on Reysi teoreem, väitmine, et on olemas algoritmiliselt lahendamatu probleem, mis määrab, kas mingi programm arvutab mingit mitte triviaalset omadust arvutavatest funktsioonidest. Näiteks ei ole võimalik kirjutada analüsaatorit, mis suudaks igast lähtekoodist määrata, kas analüüsitav programm on algoritmi rakendamine, mis arvutab näiteks tervete arvude ruutu.

Seega on staatiliste analüsaatorite funktsionaalsusel ületamatud piirangud. Staatiline analüsaator ei suuda kunagi kõigis olukordades kindlaks teha selliseid asju nagu näiteks „null pointer exception” nendes keeltes, mis lubavad nulli väärtust, või igas olukorras kindlaks teha „attribute not found” nendes keeltes, kus on dünaamiline tüpiseerimine. Kõik, mida parim staatiline analüsaator suudab, on tuvastada erandeid, mille arv kõikide võimalike probleemide seas teie lähtekoodis on ülepingutamine, meres tilgake.

Staatiline analüüs ei ole vigade otsimine

Ülaltoodust järeldub: staatiline analüüs ei ole vahend programmide defektide arvu vähendamiseks. Julgen väita, et kui seda esmakordselt teie projektis rakendada, leiab ta koodist „huvitavaid” kohti, kuid tõenäoliselt ei tuvastada ühtki defekti, mis mõjutaks teie programmi töö kvaliteeti.

Automaatsete analüsaatorite leidudest on hämmastavad näited, kuid ärge unustage, et need näited on leitud, skannides suurt hulka suures koguses koodibaase. Samuti nagu häkkerid, kellel on võimalus testida mitmeid lihtsaid paroole paljude kontode puhul, leiavad nad lõpuks need kontod, kus on lihtne parool.

Kas see tähendab, et staatilist analüüsi ei tasu rakendada? Loomulikult mitte! Ja täpselt sama põhjus, miks tasub kontrollida iga uut parooli, et see ei satuks „lihtsate” paroolide mustrisse.

Staatiline analüüs on rohkem kui vigade otsimine

Tegelikult on analüüsiga praktiliselt lahendatavad probleemid palju laiemad. Lõppkokkuvõttes on staatiline analüüs järgmine: igasugune lähtefailide kontrollimine, mis toimub enne nende käivitamist. Siin on mõned asjad, mida saab teha:

  • Koodistandardite kontrollimine laiemas mõttes. See hõlmab nii vormindamise kontrolli kui ka tühjade/ülesannete sulgude otsimist, künniste seadmist mõõdikutele nagu ridade arv/läbivaatamiste keerukuse tase jne – kõike, mis potentsiaalselt raskendab koodi loetavust ja hooldamist. Java puhul on selleks tööriistaks Checkstyle, Pythonis aga flake8. Selliseid programme nimetatakse tavaliselt "lintereiks".
  • Analüüsitav ei ole ainult käivitatav kood. Ressurssfailid, nagu JSON, YAML, XML, .properties, võivad (ja peavad!) olema automaatselt kontrollitud kehtivuse osas. Lõppude lõpuks on parem teada saada, et mingite paaritute jutumärkide tõttu on JSON-struktuur rikkunud varakult Pull Requesti automaatse kontrollimise käigus, kui testimisel või töö ajal? Vastavad tööriistad on olemas: näiteks, YAMLlint, JSONLint.
  • Kompileerimine (või parsimine dünaamilistes programmeerimiskeeltes) on samuti statilise analüüsi liik. Reeglina suudavad kompilaatorid anda hoiatusi, mis viitavad probleemidele lähtekoodi kvaliteediga, ja neid ei tohiks ignoreerida.
  • Mõnikord ei ole kompileerimine ainult käivitatava koodi kompileerimine. Näiteks, kui teie dokumentatsioon on vormingus AsciiDoctor, siis AsciiDoctor (Maven plugin) võib muuta selle HTML-iks/PDF-iks ja anda hoiatusi, näiteks rikutud sisemiste linkide kohta. Ja see on oluline põhjus, et mitte aktsepteerida Pull Requesti muudatustega dokumentatsioonis.
  • Õigekirjakontroll on samuti statilise analüüsi liik. Utiliit aspell on võimeline kontrollima õigekirja mitte ainult dokumentatsioonis, vaid ka programmide lähtekoodis (kommentaarides ja väärtustes) erinevates programmeerimiskeeltes, sealhulgas C/C++, Java ja Python. Õigekirja viga kasutajaliideses või dokumentatsioonis on samuti defekt!
  • Konfiguratsioonitestid (mida see tähendab – vt. see ja see aruandeid), kuigi neid täidetakse modulaarsete testide käitamisest nagu pytest, on tegelikult ka statilise analüüsi liik, kuna nad ei täida lähtekoodi oma täitmise käigus.

Nagu näeme, on veaotsing selle nimekirja kõige vähem oluline roll, samas kui kõik muu on kergesti kätte saadav tasuta avatud lähtekoodi tööriistade abil.

Millised nendest staatilise analüüsi tüübist tuleks teie projektis rakendada? Muidugi, kõik, mida rohkem - seda parem! Peamine on see õigesti rakendada, millest räägitakse edasi.

Tarnetoru kui mitmestastline filter ja staatiline analüüs kui selle esimene kaskaad

Pideva integreerimise klassikaline metafoor on voolikutoru (pipeline), mille kaudu liiguvad muudatused - alates lähtekoodist kuni tootmisse toimetamiseni. Selle konveieri standardne etappide jada näeb välja järgmine:

  1. staatiline analüüs
  2. kompileerimine
  3. moodulitestid
  4. integreerimistestid
  5. kasutajaliidese testid
  6. käed-ülesanne kontroll

Konveieril N-ndal etapil tagasi lükatud muudatused ei edastata etapile N+1.

Miks just nii, mitte teisiti? Testimise osas konveieril saavad testijad teada laialdaselt tuntud testimise püramiidist.

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

Selle püramiidi alumises osas on 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 neid tuleks täita esimesena. Püramiidi ülaosas on olukord vastupidine, seega peaks integreerimistestide ja kasutajaliidese testide arv olema vähendatud vajalikule miinimumile. Inimene selles ahelas on kõige kallim, aeglasem ja ebausaldusväärsem ressurss, seetõttu on ta viimases positsioonis ja teeb tööd ainult siis, kui eelnevad etapid ei leidnud defekte. Kuid selliste põhimõtete kohaselt ehitatakse konveier ka osades, mis ei ole otseselt seotud testimisega!

Tahaksin pakkuda analoogiat mitmestastilise vee filtreerimise süsteemiga. Sisse saavad määrdunud vesi (defektidega muudatused), väljundis peame saama puhtat vett, kus kõik soovimatud saasteained on eemaldatud.

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

Nagu teada, projekteeritakse puhastusfilter nii, et iga järgmine kaskaad suudab eraldada üha väiksema fraktsiooni saasteaineid. Seejuures on Coarse-filter kaskaadidel suurem läbilaskevõime ja madalam hind. Meie analoogias tähendab see, et sisendquality gates'on suurema jõudlusega, vajavad vähem pingutust käivitamiseks ja on ise töörežiimis nõudlikumad — ja just sellises järjestuses nad on paigutatud. Staatilise analüüsi roll, mis, nagu me nüüd mõistame, suudab eraldada vaid kõige jämedamad vead — on nagu riba

Staatiline analüüs iseenesest ei paranda lõppkvaliteeti, nagu

Riba eesmärk on vabastada järgmised kaskaadid kõige jämedamate vigade püüdmisest. Näiteks ei tohiks code review tegija vähemalt tähelepanu juhtida vale vorminduse ega kehtestatud kodeerimisnormide rikkumise (nt liigsete sulgude või liiga sügavate harustikeringide). NPE-viisid peaks püüdma moodulitestid, kuid kui analüsaator juba enne testi ütleb meile, et tõrge tuleb kindlasti esile — kiirendab see oluliselt selle parandamist.

Arvan, et nüüd on selge, miks staatiline analüüs ei paranda toote kvaliteeti, kui seda rakendatakse episoodiliselt, ja peaks olema pidevalt rakendatud, et tõrjuda muudatusi, millel on jämedad vead. Küsimus, kas staatilise analüsaatori rakendamine parandab teie toote kvaliteeti, on ligikaudu võrreldav küsimusega, kas

Jätkamine pärandi projekti

Oluline praktiline küsimus: kuidas rakendada staatilist analüüsi pideva integreerimise protsessis kui "quality gate"? Automaatsete testide puhul on kõik selge: on olemas testide kogum, mille ebaõnnestumine on piisav põhjus, et arvata, et ehitus ei läinud quality gate'ist läbi. Katse kehtestada gate staatilise analüüsi tulemuste põhjal ebaõnnestub: legacy-koodil on analüüsi hoiatuste arv liiga suur, neid ei saa täielikult ignoreerida, kuid toote tarnimist on võimatu peatada ainult seetõttu, et selles on analüsaatori hoiatuseid.

Esimese rakendamise korral annab analüsaator igasugustes projektides tohutul hulgal hoiatuseid, mille enamiku seos toote õigete funktsioonidega on tühine. Kõiki neid märkusi korraga parandada ei ole võimalik, ja paljusid ei peagi parandama. Lõppude lõpuks, me ju teame, et meie toode töötab kokkuvõttes kenasti, ja enne staatilise analüüsi rakendamist!

Kokkuvõttes piirdutakse paljudes juhtudes staatilise analüüsi episoodilise kasutamisega või kasutatakse seda vaid teavitamise režiimis, kus ehitamisel väljastatakse lihtsalt analüsaatori aruanne. See on ekvivalent kogu analüüsi puudumisele, sest kui meil on juba hulk hoiatuseid, jääb koodi muutmisel veel ühe (kas või tõsise) hoiatuse ilmumine märkamatuks.

On teada järgmised meetodid quality gate'ide seadmiseks:

  • Koguhulga hoiatuste või hoiatuste arvu, mis jagatakse koodiread, piirmäära seadmine. See töötab halvasti, kuna selline gate laseb vabalt läbi muudatused, kus on uued vead, kuni nende limiit on ületatud.
  • Koodi kõigi vanade hoiatuste fikseerimine kindlal hetkel kui ignoreerituks, ja uute hoiatuste ilmnemisel buildiprotsessi keeldumine. Sellist funktsionaalsust pakuvad PVS-studio ja mõned veebiteenused, näiteks Codacy. Ma pole PVS-studios töötanud, aga minu kogemus Codacyga näitab, et nende suurim probleem on see, et määrata, mis on 'vana' ja mis on 'uus' viga - see on üsna keeruline ja mitte alati õigesti toimiv algoritm, eriti kui failid muutuvad või on ümber nimetatud. Minu mäletamist mööda võis Codacy jätta uued hoiatused pull requestis tähelepanuta, samas kui nad keeldusid pull requestist hoiatuste tõttu, mis ei olnud seotud selle PR-i koodimuudatustega.
  • Minu arvates on kõige tõhusam lahendus see, mis on toodud raamatus Continuous Delivery «ratcheting» meetod. Peamine idee seisneb selles, et iga väljaande omaduseks on staatilise analüüsi hoiatuste arv, ja lubatud on vaid sellised muudatused, mis ei suurenda üldiselt hoiatuste arvu.

Ratta mehhanism

See töötab järgmiselt:

  1. Esialgses etapis pannakse väljaande metadatas kirja hoiatuste arv koodis, mille analüsaatorid on leidnud. Nii et kui põhilist haru builditakse, registreeritakse teie repoteerimishalduris mitte lihtsalt 'väljaanne 7.0.2', vaid 'väljaanne 7.0.2, mis sisaldab 100500 Checkstyle-hoiatust'. Kui kasutate arenenud repoteerimishaldurit (näiteks Artifactory), on selliste metadatade säilitamine teie väljaande kohta lihtne.
  2. Nüüd iga pull request buildimise ajal võrdleb saadud hoiatuste arvu praeguses väljaandes oleva arvuga. Kui PR toob kaasa selle arvu suurenemise, siis kood ei läbige kvaliteeditesti staatilise analüüsi osas. Kui hoiatuste arv väheneb või ei muutu, siis see läbib.
  3. Järgmise väljaande puhul pannakse uuesti arvestatud hoiatuste arv väljaande metadatas kirja.

Nii vähehaaval, kuid järjepidevalt (nagu töötades ratta kerimismehhanismiga) hakkab hoiatuste arv suunduma nulli. Muidugi saab süsteemi petta, sisestades uue hoiatusena, kuid parandades kellegi teise. See on normaalne, kuna pikaajaliselt toob see tulemusi: hoiatused parandatakse tavaliselt mitte ükshaaval, vaid kohe teatud tüüpi rühmana, ja kõik kergesti kõrvaldatavad hoiatused kaovad üsna kiiresti.

Sellel joonisel on näidatud Checkstyle'i hoiatuste koguarv kuue kuu jooksul sellise „ratta” töö ajal ühes meie OpenSource projektist. Hoiatuste arv vähenes kordades, ning see juhtus loomulikult, koos toote arendamisega!

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

Katan selle meetodi muudetud versiooni, lugedes hoiatuseid eraldi projekti moodulite ja analüüsitööriistade lõikes, genereeritud YAML-fail metaandmetega koostamise 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 saab „ratast” rakendada mistahes staatiliste analüüsitööde jaoks, toetudes mitte pistikprogrammidele ega kolmandate osapoolte tööriistadele. Iga analüsaator genereerib oma aruande lihttekstiformaadis või XML-formaadis, mida on lihtne analüüsida. Jääb vaid kirjutada vajalik loogika CI-skripti. Kuidas see on meie open source projektides Jenkins'i ja Artifactory baasil teostatud, saad vaadata siin või siin. Mõlemad näited sõltuvad raamatukogust ratchetlib: meetod countWarnings() loendab tavaliselt xml-silte failides, mille genereerivad Checkstyle ja Spotbugs, ning compareWarningMaps() rakendab selle „ratta”, visates vea juhul, kui hoiatusi mõnes kategoorias on rohkem.

Huvitav «ratta» teostuse variant on kommenteerimise, tekstiliteraalide ja dokumentatsiooni õigekirja kontrollimise analüüs aspelliga. Nagu teada, ei ole õigekirjakontrolli käigus kõik tundmatud standardileksikonile sõnad valed, need võivad olla lisatud kasutaja sõnastikku. Kui teha kasutaja sõnastik projekti lähtekoodi osaks, siis võib õigekirjakontrolli kvaliteedi tõkendi sõnastada järgmiselt: aspelli käitamine standard- ja kasutaja sõnastikuga. ei tohi leida õigekirjavigu.

Analüsaatori versiooni fikseerimise tähtsus

Kokkuvõttes tuleb märkida järgmist: ükskõik kuidas te analüüsi oma tarnetorusse rakendate, peab analüsaatori versioon olema fikseeritud. Kui lubada analüsaatori automaatne uuendamine, võivad järgmise pull requests'i koondamisel «ilma jääda» uued defektid, mis ei ole seotud koodi muutmisega, vaid sellega, et uus analüsaator suudab lihtsalt leida rohkem defekte - ja see rikub teie pull requests'i vastuvõtu protsessi. Analüsaatori uuendus peab olema teadlik tegu. Siiski, iga ehituskomponendi versiooni range fikseerimine on üldiselt vajalik nõue ja see on eraldi vestlusteema.

Järeldused

  • Statistiline analüüs ei leia teile vigu ja ei paranda teie toote kvaliteeti ühe korra rakendamise tulemusena. Positiivne efekt kvaliteedile tekib ainult pideva rakendamise läbi tarnimisprotsessi.
  • Vigade otsimine ei ole analüüsi peamine eesmärk, enamus kasulikest funktsioonidest on kergesti kättesaadavad avatud lähtekoodiga tööriistades.
  • Rakendage kvaliteedi tõkked statistilise analüüsi tulemuste põhjal tarnetoru esimesel etapil, kasutades «ratast» legacy-koodi jaoks.

Viidatud lingid

  1. Continuous Delivery
  2. A. Kudrjavtsev: Programmi analüüs: kuidas mõista, et oled hea programmeerija ettekanne erinevatest koodianalüüsi meetoditest (mitte ainult statistilistest!)

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster