Implementeer statiese ontleding in die proses, eerder as om foute daarmee te soek

Ek is geïnspireer om hierdie artikel te skryf deur 'n groot aantal materiaal oor statiese analise wat al hoe meer voorkom. Eerstens, hierdie PVS-ateljee blog, wat homself aktief op Habré bevorder met resensies van foute wat deur hul instrument in oopbronprojekte gevind is. PVS-ateljee onlangs geïmplementeer Java ondersteuning, en natuurlik die ontwikkelaars van IntelliJ IDEA, wie se ingeboude ontleder waarskynlik die mees gevorderde vir Java vandag is, kon nie wegbly nie.

Wanneer u sulke resensies lees, kry 'n mens die gevoel dat ons van 'n magiese eliksir praat: druk die knoppie, en hier is dit - 'n lys van gebreke voor jou oë. Dit blyk dat soos die ontleders verbeter, daar outomaties meer en meer foute sal wees, en die produkte wat deur hierdie robotte geskandeer word, sal beter en beter word, sonder enige moeite van ons kant.

Maar daar is geen magiese eliksirs nie. Ek wil graag praat oor wat gewoonlik nie in plasings soos "hier is die dinge wat ons robot kan vind" bespreek word nie: wat ontleders nie kan doen nie, wat is hul werklike rol en plek in die sagteware-afleweringsproses, en hoe om dit korrek te implementeer.

Implementeer statiese ontleding in die proses, eerder as om foute daarmee te soek
Ratchet (bron: wikipedia).

Wat statiese ontleders nooit kan doen nie

Wat is, vanuit 'n praktiese oogpunt, bronkode-analise? Ons voer sommige bronne in, en in 'n kort tyd (baie korter as hardlooptoetse) kry ons inligting oor ons stelsel. Die fundamentele en wiskundig onoorkomelike beperking is dat ons dus slegs 'n taamlik eng klas inligting kan bekom.

Die bekendste voorbeeld van 'n probleem wat nie deur statiese analise opgelos kan word nie, is stop probleem: dit is 'n stelling wat bewys dat dit onmoontlik is om 'n algemene algoritme te ontwikkel wat uit die bronkode van die program sal bepaal of dit in 'n beperkte tyd sal lus of eindig. 'n Uitbreiding van hierdie stelling is Rice se stellingwat verklaar dat vir enige nie-triviale eienskap van berekenbare funksies, die bepaling of 'n arbitrêre program 'n funksie met so 'n eienskap evalueer, 'n algoritmies onoplosbare probleem is. Dit is byvoorbeeld onmoontlik om 'n ontleder te skryf wat uit enige bronkode kan bepaal of die geanaliseerde program 'n implementering is van 'n algoritme wat byvoorbeeld 'n heelgetal bereken.

Die funksionaliteit van statiese ontleders het dus onoorkomelike beperkings. 'n Statiese ontleder sal nooit in alle gevalle dinge soos byvoorbeeld die voorkoms van "nulwyser-uitsondering" in nulbare tale kan bepaal nie, of in alle gevalle die voorkoms van "kenmerk nie gevind nie" in tale met dinamiese tik. Al wat die mees gevorderde statiese ontleder kan doen, is om spesiale gevalle uit te lig, waarvan die aantal, onder alle moontlike probleme met jou bronkode, sonder oordrywing 'n druppel in die see is.

Statiese analise is nie 'n soektog na foute nie

Die gevolgtrekking volg uit bogenoemde: statiese analise is nie 'n manier om die aantal defekte in 'n program te verminder nie. Ek sou waag om te sê dat wanneer dit die eerste keer op jou projek toegepas word, dit "amusante" plekke in die kode sal vind, maar heel waarskynlik geen defekte sal vind wat die kwaliteit van jou program beïnvloed nie.

Die voorbeelde van defekte wat outomaties deur die ontleders gevind word, is indrukwekkend, maar ons moet nie vergeet dat hierdie voorbeelde gevind is deur 'n groot stel groot kodebasisse te skandeer nie. Volgens dieselfde beginsel vind klappers wat verskeie eenvoudige wagwoorde op 'n groot aantal rekeninge kan probeer, uiteindelik daardie rekeninge wat 'n eenvoudige wagwoord het.

Beteken dit dat statiese analise nie gebruik moet word nie? Natuurlik nie! En presies om dieselfde rede waarom dit die moeite werd is om elke nuwe wagwoord na te gaan om in die stoplys van "eenvoudige" wagwoorde te kom.

Statiese ontleding is meer as om foute te vind

Trouens, die probleme wat prakties opgelos word deur analise is baie wyer. Statiese analise is immers in die algemeen enige kontrolering van bronkodes wat uitgevoer word voordat dit bekendgestel word. Hier is 'n paar dinge wat jy kan doen:

  • Kontroleer koderingstyl in die wydste sin van die woord. Dit sluit in beide die nagaan van formatering en die soek na die gebruik van leë/ekstra hakies, die stel van drempels op maatstawwe soos aantal lyne / siklomatiese metode kompleksiteit, ens. - alles wat kode potensieel meer leesbaar en onderhoubaar maak. In Java is hierdie instrument Checkstyle, in Python is dit flake8. Programme van hierdie klas word gewoonlik "linters" genoem.
  • Nie net uitvoerbare kode kan ontleed word nie. Hulpbronlêers soos JSON, YAML, XML, .properties kan (en moet!) outomaties nagegaan word vir geldigheid. Is dit nie beter om uit te vind dat die JSON-struktuur weens 'n paar ongepaarde aanhalings in 'n vroeë stadium van outomatiese Pull Request-validering gebreek word as wanneer toetse uitgevoer word of tydens Run-tyd nie? Toepaslike gereedskap is beskikbaar: bv. YAMLlint, JSONLint.
  • Samestelling (of ontleding vir dinamiese programmeertale) is ook 'n soort statiese analise. As 'n reël is samestellers in staat om waarskuwings uit te reik wat probleme met die kwaliteit van die bronkode aandui, en dit moet nie geïgnoreer word nie.
  • Soms gaan samestelling nie net oor die samestelling van uitvoerbare kode nie. Byvoorbeeld, as jy dokumentasie in die formaat het AsciiDokter, dan op die oomblik van sy transformasie in HTML/PDF hanteerder AsciiDoctor (Maven-inprop) kan waarskuwings uitreik, byvoorbeeld oor stukkende interne skakels. En dit is 'n goeie rede om nie die Trek-versoek met dokumentasieveranderings te aanvaar nie.
  • Speltoetsing is ook 'n tipe statiese analise. Nut aspell is in staat om spelling nie net in dokumentasie na te gaan nie, maar ook in programbronkodes (kommentaar en letterlik) in verskillende programmeertale, insluitend C/C++, Java en Python. 'n Spelfout in die gebruikerskoppelvlak of dokumentasie is ook 'n gebrek!
  • Konfigurasietoetse (sien vir wat dit is hierdie и hierdie verslae), alhoewel hulle in 'n eenheidstoets-looptyd soos pytest uitgevoer word, is dit eintlik ook 'n soort statiese analise, aangesien hulle nie bronkodes tydens hul uitvoering uitvoer nie.

Soos u kan sien, neem die vind van foute in hierdie lys die minste belangrike rol, en alles anders is beskikbaar deur die gebruik van gratis oopbronnutsgoed.

Watter van hierdie tipe statiese analise moet in jou projek gebruik word? Natuurlik, hoe meer, hoe beter! Die belangrikste ding is om dit korrek te implementeer, wat verder bespreek sal word.

Afleweringspyplyn as 'n multi-stadium filter en statiese analise as sy eerste kaskade

Die klassieke metafoor vir deurlopende integrasie is die pyplyn (pyplyn) waardeur veranderinge vloei – van die verandering van die bronkode tot aflewering tot produksie. Die standaard volgorde van stadiums van hierdie pyplyn lyk soos volg:

  1. statiese analise
  2. samestelling
  3. eenheid toetse
  4. integrasie toetse
  5. UI toetse
  6. handkontrole

Veranderinge wat in die Nde stadium van die pyplyn verwerp is, word nie na stadium N+1 voortgesit nie.

Hoekom juis so en nie anders nie? In die toetsgedeelte van die pyplyn sal toetsers die bekende toetspiramide herken.

Implementeer statiese ontleding in die proses, eerder as om foute daarmee te soek
toets piramide. Bron: статья Martin Fowler.

Aan die onderkant van hierdie piramide is toetse wat makliker is om te skryf, vinniger hardloop en nie neig na vals positief nie. Daarom moet daar meer van hulle wees, hulle moet meer kode dek en eers uitgevoer word. Aan die bokant van die piramide is die teenoorgestelde waar, dus moet die aantal integrasie- en UI-toetse tot die nodige minimum verminder word. Die persoon in hierdie ketting is die duurste, stadigste en mees onbetroubare hulpbron, so hy is heel aan die einde en verrig slegs werk as die vorige stadiums geen gebreke gevind het nie. Volgens dieselfde beginsels word die pyplyn egter in dele gebou wat nie direk met toetsing verband hou nie!

Ek wil graag 'n analogie bied in die vorm van 'n multi-stadium water filtrasie stelsel. Vuil water word aan die inset verskaf (verander met defekte), by die uitset moet ons skoon water kry, waarin alle ongewenste kontaminante uitgeskakel word.

Implementeer statiese ontleding in die proses, eerder as om foute daarmee te soek
Meerfase filter. Bron: Wikimedia Commons

Soos u weet, is skoonmaakfilters so ontwerp dat elke volgende waterval 'n steeds kleiner fraksie van kontaminante kan uitfiltreer. Terselfdertyd het growwer suiweringskaskenades 'n hoër deurset en laer koste. In ons analogie beteken dit dat die insetkwaliteithekke vinniger is, minder moeite verg om te begin, en self meer pretensieloos in werking is - en dit is presies die volgorde waarin hulle gebou is. Die rol van statiese analise, wat, soos ons nou verstaan, in staat is om slegs die mees growwe defekte uit te wis, is die rol van 'n rooster-"modder" heel aan die begin van die filterkaskade.

Statiese ontleding op sigself verbeter nie die kwaliteit van die finale produk nie, net soos 'n "modderval" nie water drinkbaar maak nie. En tog, in gemeen met ander elemente van die vervoerband, is die belangrikheid daarvan duidelik. Alhoewel in 'n multi-stadium filter, die uitset stadiums potensieel in staat is om alles dieselfde as die inset stadiums vas te vang, is dit duidelik tot watter gevolge 'n poging om oor die weg te kom met slegs fyn suiwering stadiums, sonder inset stadiums, sal lei.

Die doel van die "modderversamelaar" is om daaropvolgende kaskades af te laai van die opvang van baie growwe defekte. Byvoorbeeld, 'n kodebeoordelaar moet ten minste nie afgelei word deur verkeerd geformateerde kode en oortreding van gevestigde koderingsnorme (soos ekstra hakies of te diep geneste takke). Foute soos NPE moet deur eenheidstoetse gevang word, maar as selfs voor die toets die ontleder vir ons aandui dat die fout onvermydelik moet gebeur, sal dit die regstelling daarvan aansienlik bespoedig.

Ek dink dit is nou duidelik hoekom statiese analise nie die kwaliteit van 'n produk verbeter as dit af en toe gebruik word nie, en voortdurend gebruik moet word om veranderinge met growwe defekte uit te filter. Om te vra of die gebruik van 'n statiese ontleder die kwaliteit van jou produk sal verbeter, is min of meer gelykstaande aan die vraag "Sal die drinkkwaliteit van water wat uit 'n vuil dam geneem word, verbeter as dit deur 'n vergiettes gevoer word?"

Implementering in 'n nalatenskapprojek

'n Belangrike praktiese vraag: hoe om statiese analise in die proses van deurlopende integrasie as 'n "kwaliteitshek" in te voer? In die geval van outomatiese toetse is alles voor die hand liggend: daar is 'n stel toetse, die mislukking van enige van hulle is 'n voldoende rede om te glo dat die vergadering nie die kwaliteitshek geslaag het nie. 'n Poging om 'n hek op dieselfde manier te installeer gebaseer op die resultate van statiese analise misluk: daar is te veel ontledingswaarskuwings in die nalatenskapkode, jy wil dit nie heeltemal ignoreer nie, maar dit is ook onmoontlik om die aflewering van 'n produk net omdat dit ontlederwaarskuwings bevat.

Wanneer dit vir die eerste keer gebruik word, genereer die ontleder 'n groot aantal waarskuwings oor enige projek, waarvan die oorgrote meerderheid nie verband hou met die korrekte werking van die produk nie. Dit is onmoontlik om al hierdie opmerkings gelyktydig reg te stel, en baie van hulle is nie nodig nie. Ons weet immers dat ons produk as geheel werk, selfs voor die bekendstelling van statiese analise!

Gevolglik beperk baie mense hulself tot episodiese gebruik van statiese analise, of gebruik dit slegs in inligtingsmodus, wanneer die ontlederverslag bloot tydens samestelling uitgereik word. Dit is gelykstaande aan die afwesigheid van enige ontleding, want as ons reeds baie waarskuwings het, dan gaan die voorkoms van 'n ander een (maak nie saak hoe ernstig nie) wanneer die kode verander ongemerk.

Die volgende maniere om kwaliteithekke bekend te stel, is bekend:

  • Stel 'n limiet op die totale aantal waarskuwings, of die aantal waarskuwings gedeel deur die aantal reëls kode. Dit werk nie goed nie, want so 'n hek slaan vrylik veranderinge met nuwe defekte oor totdat hul limiet oorskry word.
  • Op 'n sekere punt herstel alle ou waarskuwings in die kode as geïgnoreer, en misluk die bou wanneer nuwe waarskuwings voorkom. Hierdie funksionaliteit word verskaf deur PVS-ateljee en sommige aanlynbronne, soos Codacy. Ek het nie 'n kans gehad om in PVS-ateljee te werk nie, wat my ervaring met Codacy betref, hul grootste probleem is dat die definisie van wat 'n "ou" en wat 'n "nuwe" fout is 'n taamlik ingewikkelde algoritme is wat nie werk altyd korrek, veral as lêers sterk gewysig of hernoem word. In my geheue kon Codacy nuwe waarskuwings in 'n trekversoek oorslaan, en terselfdertyd nie 'n trekversoek oorslaan nie as gevolg van waarskuwings wat nie verband hou met veranderinge in die kode van hierdie PR nie.
  • Myns insiens word die doeltreffendste oplossing in die boek beskryf deurlopende aflewering "ratcheting" metode. Die hoofgedagte is dat 'n eienskap van elke vrystelling die aantal statiese analise-waarskuwings is, en slegs veranderinge wat nie die totale aantal waarskuwings verhoog nie, word toegelaat.

Ratchet

Dit werk so:

  1. In die aanvanklike stadium word 'n rekord in die vrystelling-metadata van die aantal waarskuwings in die kode wat deur die ontleders gevind is, geïmplementeer. Dus, wanneer jy stroomop bou, word jou bewaarplekbestuurder nie net "vrystelling 7.0.2" geskryf nie, maar "vrystelling 7.0.2 wat 100500 Checkstyle-waarskuwings bevat". As jy 'n gevorderde bewaarplekbestuurder (soos Artifactory) gebruik, is dit maklik om sulke metadata oor jou vrystelling te stoor.
  2. Nou vergelyk elke trekversoek op bou die aantal waarskuwings wat dit ontvang met die getal in die huidige vrystelling. As PR lei tot 'n toename in hierdie getal, dan slaag die kode nie die kwaliteitshek in statiese analise nie. As die aantal waarskuwings afneem of nie verander nie, gaan dit verby.
  3. Met die volgende vrystelling sal die herberekende aantal waarskuwings na die vrystellingmetadata teruggeskryf word.

So bietjie vir bietjie, maar bestendig (soos met 'n ratel), sal die aantal waarskuwings na nul neig. Natuurlik kan die stelsel geflous word deur 'n nuwe waarskuwing in te stel, maar iemand anders s'n reg te stel. Dit is normaal, want op die lange duur gee dit die resultaat: waarskuwings word as 'n reël nie een vir een reggestel nie, maar onmiddellik deur 'n groep van 'n sekere soort, en alle maklik uitgeskakelde waarskuwings word vinnig uitgeskakel.

Hierdie grafiek toon die totale aantal Checkstyle-waarskuwings vir ses maande se werking van so 'n "ratchet" op een van ons oopbronprojekte. Die aantal waarskuwings het met 'n orde van grootte afgeneem, en dit het natuurlik gebeur, parallel met die ontwikkeling van die produk!

Implementeer statiese ontleding in die proses, eerder as om foute daarmee te soek

Ek gebruik 'n gewysigde weergawe van hierdie metode, wat afsonderlik waarskuwings tel deur projekmodule en analise-instrument, wat lei tot 'n YAML-lêer met samestelling-metadata wat iets soos volg lyk:

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 enige gevorderde CI-stelsel kan 'n ratel vir enige statiese analise-instrumente geïmplementeer word sonder om op inproppe en derdeparty-instrumente staat te maak. Elkeen van die ontleders produseer sy verslag in 'n eenvoudige teks- of XML-formaat wat maklik is om te ontleed. Dit bly om net die nodige logika in die CI-skrip te registreer. Jy kan sien hoe dit geïmplementeer word in ons oopbronprojekte gebaseer op Jenkins en Artifactory, jy kan hier of hier. Beide voorbeelde is biblioteekafhanklik ratchetlib: metode countWarnings() tel xml-etikette in lêers wat op die gewone manier deur Checkstyle en Spotbugs gegenereer word, en compareWarningMaps() implementeer dieselfde ratel, wat 'n fout gooi wanneer die aantal waarskuwings in enige van die kategorieë toeneem.

'n Interessante ratel-implementering is moontlik vir spelontleding van opmerkings, teksletters en dokumentasie deur aspell te gebruik. Soos u weet, wanneer u spelling nagaan, is nie alle woorde wat aan die standaardwoordeboek bekend is, verkeerd nie, dit kan by die gebruikerswoordeboek gevoeg word. As jy die gebruikerswoordeboek deel van die projekbronkode maak, dan kan die spelkwaliteithek soos volg geformuleer word: aspell uitvoering met die standaard en gebruikerswoordeboek behoort nie vind geen spelfoute nie.

Oor die belangrikheid daarvan om die ontlederweergawe reg te stel

Ten slotte moet daar op die volgende gelet word: maak nie saak hoe jy die ontleding in jou afleweringspyplyn implementeer nie, die weergawe van die ontleder moet reggemaak word. As jy toelaat dat die ontleder spontaan opdateer, wanneer die volgende trekversoek gebou word, kan nuwe defekte "na vore kom" wat nie verband hou met kodeveranderings nie, maar verband hou met die feit dat die nuwe ontleder eenvoudig meer defekte kan vind - en dit sal die proses van die aanvaarding van trekversoeke verbreek. Die opgradering van die ontleder moet 'n bewustelike aksie wees. Die harde vasstelling van die weergawe van elke samestelling komponent is egter 'n noodsaaklike vereiste in die algemeen en 'n onderwerp vir 'n aparte bespreking.

Bevindinge

  • Statiese ontleding sal nie foute vir jou vind nie en sal nie die kwaliteit van jou produk verbeter as gevolg van 'n enkele toepassing nie. Die enigste positiewe effek op kwaliteit is die voortdurende gebruik daarvan tydens die afleweringsproses.
  • Om foute te vind is glad nie die hooftaak van analise nie, die oorgrote meerderheid nuttige funksies is beskikbaar in oopbronnutsgoed.
  • Implementeer kwaliteithekke gebaseer op die resultate van statiese analise in die heel eerste stadium van die afleweringspyplyn, met behulp van 'n ratel vir nalatenskapkode.

verwysings

  1. deurlopende aflewering
  2. A. Kudryavtsev: Programanalise: hoe om te verstaan ​​dat jy 'n goeie programmeerder is verslag te doen oor verskillende metodes van kode-analise (nie net staties nie!)

Bron: will.com

Voeg 'n opmerking