Implementujte do procesu statickou analýzu místo toho, abyste ji používali k hledání chyb

K napsání tohoto článku mě přimělo velké množství materiálů o statické analýze, které se stále více dostávají do mé pozornosti. Za prvé, toto PVS-studio blog, která se na Habré aktivně propaguje pomocí recenzí chyb nalezených jejich nástrojem v open source projektech. Nedávno implementováno PVS-studio podpora Javaa samozřejmě vývojáři IntelliJ IDEA, jejichž vestavěný analyzátor je dnes pravděpodobně nejpokročilejší pro Javu, nemohl zůstat stranou.

Při čtení takových recenzí máte pocit, že mluvíme o magickém elixíru: stiskněte tlačítko, a tady to je - seznam vad před vašima očima. Zdá se, že jak se analyzátory zdokonalují, bude automaticky nacházet stále více chyb a produkty naskenované těmito roboty budou stále lepší a lepší, bez jakéhokoli úsilí z naší strany.

Ale neexistují žádné magické elixíry. Chtěl bych mluvit o tom, o čem se obvykle nemluví v příspěvcích typu „tady jsou věci, které náš robot dokáže najít“: co analyzátory nemohou dělat, jaká je jejich skutečná role a místo v procesu dodávání softwaru a jak je správně implementovat .

Implementujte do procesu statickou analýzu místo toho, abyste ji používali k hledání chyb
Ratchet (zdroj: Wikipedia).

Co statické analyzátory nikdy nedokážou

Co je analýza zdrojového kódu z praktického hlediska? Poskytujeme nějaký zdrojový kód jako vstup a jako výstup, během krátké doby (mnohem kratší než spuštění testů) získáme nějaké informace o našem systému. Zásadním a matematicky nepřekonatelným omezením je, že tímto způsobem můžeme získat jen dosti úzkou třídu informací.

Nejznámějším příkladem problému, který nelze vyřešit pomocí statické analýzy, je problém s vypnutím: Toto je teorém, který dokazuje, že je nemožné vyvinout obecný algoritmus, který by dokázal ze zdrojového kódu programu určit, zda se zacyklí nebo skončí v konečném čase. Rozšířením této věty je Riceova věta, který uvádí, že pro jakoukoli netriviální vlastnost vyčíslitelných funkcí je určení, zda libovolný program vyhodnocuje funkci s takovou vlastností, algoritmicky neřešitelný problém. Například je nemožné napsat analyzátor, který dokáže z libovolného zdrojového kódu určit, zda je analyzovaný program implementací algoritmu, který vypočítává řekněme druhou mocninu celého čísla.

Funkčnost statických analyzátorů má tedy nepřekonatelná omezení. Statický analyzátor nikdy nebude schopen ve všech případech detekovat takové věci, jako je například výskyt „výjimky ukazatele null“ v jazycích, které umožňují hodnotu null, nebo ve všech případech určit výskyt „ atribut nenalezen" v dynamicky zadávaných jazycích. Vše, co nejpokročilejší statický analyzátor umí, je upozornit na speciální případy, jejichž počet mezi všemi možnými problémy s vaším zdrojovým kódem je bez nadsázky kapka v pytli.

Statická analýza není o hledání chyb

Z výše uvedeného vyplývá závěr: statická analýza není prostředkem ke snížení počtu defektů v programu. Troufnul bych si říci: při první aplikaci na váš projekt najde „zajímavá“ místa v kódu, ale s největší pravděpodobností nenajde žádné vady, které ovlivňují kvalitu vašeho programu.

Příklady defektů automaticky nalezených analyzátory jsou působivé, ale neměli bychom zapomínat, že tyto příklady byly nalezeny skenováním velké sady velkých kódových bází. Na stejném principu hackeři, kteří mají možnost vyzkoušet několik jednoduchých hesel na velkém počtu účtů, nakonec najdou účty, které mají jednoduché heslo.

Znamená to, že statická analýza by se neměla používat? Samozřejmě že ne! A přesně ze stejného důvodu, pro který stojí za to zkontrolovat každé nové heslo, abyste se ujistili, že je zahrnuto do seznamu stop „jednoduchých“ hesel.

Statická analýza je více než hledání chyb

Ve skutečnosti jsou problémy prakticky řešené analýzou mnohem širší. Statická analýza je totiž obecně jakékoli ověřování zdrojových kódů prováděné před jejich spuštěním. Zde je několik věcí, které můžete udělat:

  • Kontrola stylu kódování v nejširším slova smyslu. To zahrnuje jak kontrolu formátování, hledání použití prázdných/nadbytečných závorek, nastavení prahových hodnot pro metriky, jako je počet řádků/cyklomatická složitost metody atd. – cokoli, co potenciálně brání čitelnosti a udržovatelnosti kódu. V Javě je takovým nástrojem Checkstyle, v Pythonu - flake8. Programy této třídy se obvykle nazývají „linters“.
  • Analyzovat lze nejen spustitelný kód. Soubory zdrojů jako JSON, YAML, XML, .properties mohou (a měly by!) být automaticky kontrolovány na platnost. Koneckonců, je lepší zjistit, že struktura JSON je narušena kvůli některým nespárovaným uvozovkám v rané fázi automatického ověření požadavku Pull než během provádění testu nebo běhu? K dispozici jsou vhodné nástroje: např. YAMLlint, JSONLint.
  • Kompilace (nebo analýza pro dynamické programovací jazyky) je také druh statické analýzy. Obecně jsou kompilátory schopny produkovat varování, která indikují problémy s kvalitou zdrojového kódu a neměla by být ignorována.
  • Někdy je kompilace více než jen kompilace spustitelného kódu. Pokud máte například dokumentaci ve formátu AsciiDoctor, poté v okamžiku převedení do HTML/PDF obslužný program AsciiDoctor (plugin Maven) může vydávat varování, například o nefunkčních interních odkazech. A to je dobrý důvod, proč nepřijmout žádost o stažení se změnami dokumentace.
  • Kontrola pravopisu je také druh statické analýzy. Utility kouzlo je schopen kontrolovat pravopis nejen v dokumentaci, ale také ve zdrojových kódech programu (komentáře a literály) v různých programovacích jazycích včetně C/C++, Java a Python. Vadou je i pravopisná chyba v uživatelském rozhraní nebo dokumentaci!
  • Konfigurační testy (o čem jsou - viz. tento и tento reporty), i když jsou prováděny v běhovém prostředí jednotkového testu, jako je pytest, jsou ve skutečnosti také typem statické analýzy, protože během svého provádění nespouštějí zdrojové kódy.

Jak vidíte, hledání chyb v tomto seznamu hraje nejméně důležitou roli a vše ostatní je dostupné pomocí bezplatných open source nástrojů.

Které z těchto typů statické analýzy byste měli ve svém projektu použít? Samozřejmě čím více, tím lépe! Hlavní věc je správně implementovat, o čemž se bude dále diskutovat.

Dodávací potrubí jako vícestupňový filtr a statická analýza jako jeho první stupeň

Klasická metafora pro kontinuální integraci je potrubí, kterým probíhají změny, od změn zdrojového kódu přes dodání až po produkci. Standardní sekvence fází v tomto potrubí vypadá takto:

  1. statická analýza
  2. kompilace
  3. jednotkové testy
  4. integrační testy
  5. UI testy
  6. ruční kontrola

Změny zamítnuté v N. etapě potrubí se nepřenášejí do etapy N+1.

Proč zrovna takhle a ne jinak? V testovací části potrubí poznají testeři známou testovací pyramidu.

Implementujte do procesu statickou analýzu místo toho, abyste ji používali k hledání chyb
Zkušební pyramida. Zdroj: článek Martin Fowler.

Na konci této pyramidy jsou testy, které se snadněji píší, rychleji provádějí a nemají tendenci selhat. Proto by jich mělo být více, měly by pokrývat více kódu a měly by být provedeny jako první. Na vrcholu pyramidy je tomu naopak, takže počet integračních a UI testů by měl být zredukován na nezbytné minimum. Osoba v tomto řetězci je nejdražším, nejpomalejším a nespolehlivějším zdrojem, takže je na samém konci a práci provede pouze v případě, že předchozí fáze nenalezly žádné závady. Stejné principy se však používají pro stavbu potrubí v částech, které přímo nesouvisejí s testováním!

Rád bych nabídl obdobu v podobě vícestupňového systému filtrace vody. Na vstup je přiváděna špinavá voda (mění se závadami), na výstupu musíme přijímat čistou vodu, ve které jsou odstraněny všechny nežádoucí nečistoty.

Implementujte do procesu statickou analýzu místo toho, abyste ji používali k hledání chyb
Vícestupňový filtr. Zdroj: Wikimedia Commons

Jak víte, čistící filtry jsou navrženy tak, aby každá následující kaskáda mohla odfiltrovat stále jemnější frakci nečistot. Hrubší čistící kaskády mají zároveň vyšší propustnost a nižší cenu. V naší analogii to znamená, že brány kvality vstupu jsou rychlejší, vyžadují méně úsilí při startu a jsou samy o sobě nenáročnější v provozu – a to je pořadí, ve kterém jsou stavěny. Role statické analýzy, která, jak nyní chápeme, je schopna odstranit jen ty nejhrubší vady, je role „bahenní“ mřížky na samém začátku filtrační kaskády.

Statická analýza sama o sobě nezlepší kvalitu konečného produktu, stejně jako „bahenní filtr“ neudělá vodu pitnou. A přesto je ve spojení s dalšími prvky potrubí jeho důležitost zřejmá. Ačkoli ve vícestupňovém filtru jsou výstupní stupně potenciálně schopny zachytit vše, co dělají vstupní stupně, je jasné, jaké důsledky bude mít pokus vystačit se samotnými stupni jemného čištění bez vstupních stupňů.

Účelem „lapače bahna“ je odlehčit následným kaskádám od zachycení velmi hrubých defektů. Osoba provádějící kontrolu kódu by například neměla být rozptylována nesprávně naformátovaným kódem a porušením zavedených standardů kódování (jako jsou nadbytečné závorky nebo příliš hluboce vnořené větve). Chyby jako NPE by měly být zachyceny jednotkovými testy, ale pokud nám ještě před testem analyzátor naznačí, že k chybě nutně dojde, výrazně to urychlí její opravu.

Domnívám se, že je nyní jasné, proč statická analýza nezlepší kvalitu produktu, pokud se používá příležitostně, a měla by být používána neustále k odfiltrování změn s hrubými vadami. Otázka, zda použití statického analyzátoru zlepší kvalitu vašeho produktu, je zhruba ekvivalentní otázce: „Zlepší se kvalita pitné vody odebrané ze špinavého rybníka, pokud bude procházet cedníkem?“

Implementace do staršího projektu

Důležitá praktická otázka: jak implementovat statickou analýzu do procesu kontinuální integrace jako „bránu kvality“? V případě automatických testů je vše zřejmé: existuje soubor testů, selhání kteréhokoli z nich je dostatečným důvodem k domněnce, že montáž neprošla branou kvality. Pokus o instalaci brány stejným způsobem na základě výsledků statické analýzy selže: ve starším kódu je příliš mnoho varování analýzy, nechcete je úplně ignorovat, ale je také nemožné zastavit odesílání produktu jen proto, že obsahuje varování analyzátoru.

Při prvním použití vytváří analyzátor velké množství varování na jakémkoli projektu, z nichž velká většina nesouvisí se správnou funkcí produktu. Není možné opravit všechny tyto komentáře najednou a mnohé z nich nejsou nutné. Ostatně víme, že náš produkt jako celek funguje, ještě před zavedením statické analýzy!

V důsledku toho jsou mnohé omezeny na příležitostné použití statické analýzy nebo ji používají pouze v informačním režimu, kdy je zpráva analyzátoru jednoduše vydána během montáže. To je ekvivalentní absenci jakékoli analýzy, protože pokud již máme mnoho varování, pak výskyt dalšího (bez ohledu na to, jak závažného) při změně kódu zůstane bez povšimnutí.

Jsou známy následující způsoby zavádění kvalitních bran:

  • Nastavení limitu celkového počtu varování nebo počtu varování děleného počtem řádků kódu. To funguje špatně, protože taková brána volně propouští změny s novými defekty, pokud není překročen jejich limit.
  • Oprava, v určitém okamžiku, všechna stará varování v kódu jako ignorovaná, a odmítnutí sestavení, když se objeví nová varování. Tuto funkci poskytuje PVS-studio a některé online zdroje, například Codacy. Neměl jsem možnost pracovat v PVS-studiu, pokud jde o mé zkušenosti s Codacy, jejich hlavním problémem je, že určení, co je „stará“ a co „nová“ chyba, je poměrně složitý algoritmus, který ne vždy funguje správně, zejména pokud jsou soubory silně změněny nebo přejmenovány. Podle mých zkušeností mohl Codacy ignorovat nová varování v požadavku na stažení a zároveň neprošel požadavek na stažení kvůli varováním, která nesouvisela se změnami v kódu daného PR.
  • Podle mě je nejúčinnější řešení popsané v knize Continuous Delivery „ráčnovou metodou“. Základní myšlenkou je, že počet varování statické analýzy je vlastností každého vydání a jsou povoleny pouze změny, které nezvyšují celkový počet varování.

Ráčna

Funguje to takto:

  1. V počáteční fázi je v metadatech vytvořen záznam o vydání počtu varování v kódu nalezeném analyzátory. Takže když vytváříte upstream, váš správce úložiště nenapíše jen „vydání 7.0.2“, ale „vydání 7.0.2 obsahující 100500 XNUMX varování kontrolního stylu“. Pokud používáte pokročilého správce úložiště (jako je Artifactory), ukládání takových metadat o vašem vydání je snadné.
  2. Nyní každý požadavek na stažení, když je vytvořen, porovnává počet výsledných varování s počtem varování dostupných v aktuální verzi. Pokud PR vede ke zvýšení tohoto počtu, pak kód neprojde branou kvality pro statickou analýzu. Pokud se počet varování sníží nebo se nezmění, pak projde.
  3. Při příštím vydání bude přepočtený počet varování znovu zaznamenán v metadatech vydání.

Postupně, ale neustále (jako když funguje ráčna), bude mít počet varování tendenci k nule. Systém lze samozřejmě oklamat zavedením nového varování, ale opravou někoho jiného. To je normální, protože na velkou vzdálenost to dává výsledky: varování se zpravidla opravují ne jednotlivě, ale ve skupině určitého typu najednou, a všechna snadno odstranitelná varování jsou poměrně rychle odstraněna.

Tento graf ukazuje celkový počet varování Checkstyle za šest měsíců provozu takové „ráčny“. jeden z našich OpenSource projektů. Počet varování se řádově snížil a stalo se tak přirozeně souběžně s vývojem produktu!

Implementujte do procesu statickou analýzu místo toho, abyste ji používali k hledání chyb

Používám upravenou verzi této metody, která samostatně počítá varování podle projektového modulu a analytického nástroje, který vytváří soubor YAML s metadaty sestavení, která vypadá asi takto:

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

V jakémkoli pokročilém CI systému lze ratchet implementovat pro jakékoli nástroje statické analýzy, aniž byste se spoléhali na pluginy a nástroje třetích stran. Každý analyzátor vytváří svou vlastní zprávu v jednoduchém textovém nebo XML formátu, kterou lze snadno analyzovat. Zbývá jen napsat potřebnou logiku do CI skriptu. Můžete vidět, jak je to implementováno v našich open source projektech založených na Jenkins a Artifactory zde nebo zde. Oba příklady závisí na knihovně ratchetlib: metoda countWarnings() počítá xml tagy v souborech generovaných Checkstyle a Spotbugs obvyklým způsobem a compareWarningMaps() implementuje stejnou ráčnu a vyvolá chybu, když se počet varování v kterékoli z kategorií zvýší.

Zajímavá implementace "řehtačky" je možná pro analýzu pravopisu komentářů, textových literálů a dokumentace pomocí aspell. Jak víte, při kontrole pravopisu nejsou všechna slova neznámá standardnímu slovníku nesprávná, lze je přidat do uživatelského slovníku. Pokud vytvoříte vlastní slovník součástí zdrojového kódu projektu, pak lze bránu kvality pravopisu formulovat takto: spuštění aspellu se standardním a vlastním slovníkem by neměl nenajdete žádné pravopisné chyby.

O důležitosti opravy verze analyzátoru

Závěrem je třeba poznamenat, že bez ohledu na to, jak implementujete analýzu do svého distribučního kanálu, verze analyzátoru musí být opravena. Pokud analyzátoru umožníte spontánní aktualizaci, pak se při sestavování dalšího požadavku na stažení mohou „objevit“ nové defekty, které nesouvisí se změnami kódu, ale souvisejí s tím, že nový analyzátor je prostě schopen najít více defektů - a to přeruší váš proces přijímání požadavků na stažení. Aktualizace analyzátoru by měla být vědomá akce. Pevná fixace verze každé součásti sestavy je však obecně nezbytným požadavkem a tématem pro samostatnou diskusi.

Závěry

  • Statická analýza za vás nenajde chyby a nezlepší kvalitu vašeho produktu v důsledku jediné aplikace. Pozitivního vlivu na kvalitu lze dosáhnout pouze jeho neustálým používáním během procesu dodávky.
  • Hledání chyb není vůbec hlavním úkolem analýzy, drtivá většina užitečných funkcí je dostupná v opensource nástrojích.
  • Implementujte brány kvality na základě výsledků statické analýzy v úplně první fázi dodávky pomocí „ráčny“ pro starší kód.

reference

  1. Continuous Delivery
  2. A. Kudryavtsev: Analýza programu: jak pochopit, že jste dobrý programátor zpráva o různých metodách analýzy kódu (nejen statické!)

Zdroj: www.habr.com

Přidat komentář