Спроведување на статичка анализа во процесот, наместо да барате грешки со неа

Бев поттикнат да ја напишам оваа статија поради големиот број материјали за статичка анализа кои се повеќе ми привлекуваат внимание. Прво, ова Блог на PVS-студио, кој активно се промовира на Habré со помош на прегледи на грешки пронајдени од нивната алатка во проекти со отворен код. Неодамна имплементирано PVS-студио Јава поддршкаи, се разбира, развивачите на IntelliJ IDEA, чиј вграден анализатор е веројатно најнапредниот за Java денес, не можеше да се држи настрана.

Кога читате вакви критики, добивате чувство дека зборуваме за магичен еликсир: притиснете го копчето и еве го - список на дефекти пред вашите очи. Се чини дека како што се подобруваат анализаторите, автоматски ќе се пронаоѓаат се повеќе грешки, а производите скенирани од овие роботи ќе стануваат се подобри и подобри, без никаков напор од наша страна.

Но, нема магични еликсири. Би сакал да зборувам за она за што обично не се зборува во објави како „еве што може да ги најде нашиот робот“: што не можат да направат анализаторите, која е нивната вистинска улога и место во процесот на испорака на софтверот и како правилно да се имплементираат .

Спроведување на статичка анализа во процесот, наместо да барате грешки со неа
Рацет (извор: Википедија).

Она што статичните анализатори никогаш не можат да го направат

Што е анализа на изворниот код, од практична гледна точка? Ние обезбедуваме некој изворен код како влез, а како излез, за ​​кратко време (многу пократко од тестовите што се извршуваат) добиваме некои информации за нашиот систем. Основното и математички непремостливо ограничување е дека на овој начин можеме да добиеме само прилично тесна класа на информации.

Најпознатиот пример за проблем што не може да се реши со помош на статичка анализа е проблем со исклучување: Ова е теорема која докажува дека е невозможно да се развие општ алгоритам кој може да одреди од изворниот код на програмата дали ќе се врти или ќе заврши во одредено време. Продолжување на оваа теорема е Теорема на Рајс, кој вели дека за кое било нетривијално својство на пресметливи функции, определувањето дали произволна програма оценува функција со такво својство е алгоритамски нерешлив проблем. На пример, невозможно е да се напише анализатор кој може да одреди од кој било изворен код дали програмата што се анализира е имплементација на алгоритам кој пресметува, да речеме, квадратурата на цел број.

Така, функционалноста на статичките анализатори има непремостливи ограничувања. Статичкиот анализатор никогаш нема да може да открие во сите случаи такви работи како што е, на пример, појавата на „исклучок од нула покажувач“ на јазиците што дозволуваат вредноста на нула, или во сите случаи да ја одреди појавата на „ атрибутот не е пронајден“ во динамички напишани јазици. Сè што може да направи најнапредниот статички анализатор е да истакне специјални случаи, чиј број, меѓу сите можни проблеми со вашиот изворен код, е, без претерување, капка во океанот.

Статичката анализа не е за наоѓање грешки

Од горенаведеното, следува заклучокот: статичката анализа не е средство за намалување на бројот на дефекти во програмата. Би се осмелил да кажам: кога ќе се примени на вашиот проект за прв пат, ќе најде „интересни“ места во кодот, но, најверојатно, нема да најде никакви дефекти што влијаат на квалитетот на вашата програма.

Примерите на дефекти кои автоматски ги откриваат анализаторите се импресивни, но не треба да заборавиме дека овие примери се пронајдени со скенирање на голем сет на големи бази на кодови. По истиот принцип, хакерите кои имаат можност да пробаат неколку едноставни лозинки на голем број сметки, на крајот ги наоѓаат оние сметки кои имаат едноставна лозинка.

Дали ова значи дека не треба да се користи статичка анализа? Се разбира не! И токму од истата причина поради која вреди да се провери секоја нова лозинка за да се уверите дека е вклучена во стоп листата на „едноставни“ лозинки.

Статичката анализа е повеќе од пронаоѓање грешки

Всушност, проблемите што практично се решаваат со анализа се многу пошироки. На крајот на краиштата, генерално, статичката анализа е секоја проверка на изворните кодови извршена пред да бидат лансирани. Еве неколку работи што можете да ги направите:

  • Проверка на стилот на кодирање во најширока смисла на зборот. Ова вклучува и проверка на форматирање, барање употреба на празни/дополнителни загради, поставување прагови на метрика како број на линии/цикломатска сложеност на методот итн. - сè што потенцијално ја попречува читливоста и одржливоста на кодот. Во Јава, таква алатка е Checkstyle, во Python - flake8. Програмите од оваа класа обично се нарекуваат „линтери“.
  • Не само што може да се анализира извршниот код. Датотеките со ресурси како што се JSON, YAML, XML, .properties може (и треба!) автоматски да се проверат за валидност. На крајот на краиштата, подобро е да откриеме дека структурата на JSON е скршена поради некои неспарени цитати во раната фаза на автоматската проверка на барањето за повлекување отколку за време на извршувањето на тестот или времето на извршување? Достапни се соодветни алатки: на пр. YAMLlint, JSONLint.
  • Компилацијата (или парсирањето за динамични програмски јазици) е исто така еден вид статичка анализа. Општо земено, компајлерите се способни да произведуваат предупредувања што укажуваат на проблеми со квалитетот на изворниот код и не треба да се игнорираат.
  • Понекогаш компилацијата е повеќе од само компајлирање на извршна шифра. На пример, ако имате документација во формат АсииДоктор, потоа во моментот на претворање во HTML/PDF управувачот AsciiDoctor (Приклучок за Maven) може да издава предупредувања, на пример, за скршени внатрешни врски. И ова е добра причина да не го прифатите барањето за повлекување со промени во документацијата.
  • Проверката на правопис е исто така еден вид статичка анализа. Алатка аспел може да го проверува правописот не само во документацијата, туку и во програмските изворни кодови (коментари и буквални) на различни програмски јазици, вклучувајќи C/C++, Java и Python. Правописна грешка во корисничкиот интерфејс или документацијата е исто така дефект!
  • Тестови за конфигурација (за тоа што се - видете. овој и овој извештаи), иако се извршуваат во единечно тестирање, како што е pytest, всушност се исто така еден вид статичка анализа, бидејќи тие не извршуваат изворни кодови за време на нивното извршување.

Како што можете да видите, пребарувањето за грешки во оваа листа игра најмалку важна улога, а сè друго е достапно со користење на бесплатни алатки со отворен код.

Кој од овие видови статичка анализа треба да го користите во вашиот проект? Се разбира, колку повеќе толку подобро! Главната работа е правилно да се спроведе, што ќе се дискутира понатаму.

Цевковод за испорака како повеќестепен филтер и статичка анализа како негова прва фаза

Класичната метафора за континуирана интеграција е цевковод низ кој течат промените, од промена на изворниот код до испорака до производство. Стандардната низа на фази во овој гасовод изгледа вака:

  1. статичка анализа
  2. компилација
  3. единица тестови
  4. тестови за интеграција
  5. UI тестови
  6. рачна проверка

Промените што се отфрлени во N-тата фаза од гасоводот не се пренесуваат во фаза N+1.

Зошто токму на овој начин, а не поинаку? Во делот за тестирање на гасоводот, тестерите ќе ја препознаат добро познатата пирамида за тестирање.

Спроведување на статичка анализа во процесот, наместо да барате грешки со неа
Тест пирамида. Извор: Член Мартин Фаулер.

На дното на оваа пирамида има тестови кои се полесни за пишување, побрзи за извршување и немаат тенденција за неуспех. Затоа, треба да ги има повеќе, тие треба да покриваат повеќе код и прво да се извршат. На врвот на пирамидата, спротивното е точно, така што бројот на тестови за интеграција и UI треба да се намали на потребниот минимум. Лицето во овој синџир е најскапиот, бавен и несигурен ресурс, па затоа е на самиот крај и ја извршува работата само ако претходните фази не нашле дефекти. Сепак, истите принципи се користат за изградба на гасовод во делови кои не се директно поврзани со тестирањето!

Би сакал да понудам аналогија во форма на повеќестепен систем за филтрирање на вода. Нечиста вода (промени со дефекти) се доставува до влезот на излезот мора да добиеме чиста вода, во која се елиминирани сите несакани загадувачи.

Спроведување на статичка анализа во процесот, наместо да барате грешки со неа
Филтер со повеќе фази. Извор: Заедничката Ризница

Како што знаете, филтрите за чистење се дизајнирани така што секоја следна каскада може да филтрира сè пофина фракција на загадувачи. Во исто време, каскадите на погрубо прочистување имаат поголема пропусност и пониска цена. Според нашата аналогија, тоа значи дека портите за квалитет на влезот се побрзи, бараат помалку напор за стартување и самите се понепретенциозни во работењето - и тоа е редоследот по кој се изградени. Улогата на статичката анализа, која, како што сега разбираме, е способна да ги отстрани само најгрубите дефекти, е улогата на мрежата „кал“ на самиот почеток на филтерската каскада.

Статичката анализа сама по себе не го подобрува квалитетот на финалниот производ, исто како што „филтерот за кал“ не ја прави водата питка. А сепак, во врска со другите елементи на гасоводот, неговата важност е очигледна. Иако во повеќестепениот филтер, излезните фази се потенцијално способни да доловат сè што прават влезните фази, јасно е какви последици ќе произлезат од обидот да се задоволат само фазите на фино прочистување, без влезни фази.

Целта на „замката за кал“ е да ги ослободи следните каскади од фаќање многу груби дефекти. На пример, минимум, лицето кое го прави прегледот на кодот не треба да биде одвлечено од неправилно форматиран код и прекршување на воспоставените стандарди за кодирање (како дополнителни загради или премногу длабоко вгнездени гранки). Грешки како NPE треба да се фатат со единечни тестови, но ако дури и пред тестот анализаторот ни покаже дека сигурно ќе се случи грешка, тоа значително ќе го забрза неговото поправање.

Верувам дека сега е јасно зошто статичката анализа не го подобрува квалитетот на производот ако се користи повремено и треба постојано да се користи за филтрирање на промените со груби дефекти. Прашањето дали користењето статички анализатор ќе го подобри квалитетот на вашиот производ е приближно еквивалентно на прашањето: „Дали водата земена од валкано езерце ќе се подобри во квалитетот на пиење ако се помине низ цедалка?“

Имплементација во наследен проект

Важно практично прашање: како да се имплементира статичката анализа во процесот на континуирана интеграција како „порта за квалитет“? Во случај на автоматски тестови, сè е очигледно: има збир на тестови, неуспехот на кој било од нив е доволна причина да се верува дека склопот не ја поминал портата за квалитет. Обидот да се инсталира капија на ист начин врз основа на резултатите од статичката анализа не успева: има премногу предупредувања за анализа во наследниот код, не сакате целосно да ги игнорирате, но исто така е невозможно да престанете да испраќате производ само затоа што содржи предупредувања од анализаторот.

Кога се користи за прв пат, анализаторот произведува огромен број предупредувања за кој било проект, од кои огромното мнозинство не се поврзани со правилното функционирање на производот. Невозможно е да се поправат сите овие коментари одеднаш, а многу не се потребни. На крајот на краиштата, знаеме дека нашиот производ како целина функционира, дури и пред да воведеме статичка анализа!

Како резултат на тоа, многумина се ограничени на повремена употреба на статичка анализа или ја користат само во режим на информации, кога извештајот од анализаторот едноставно се издава за време на склопувањето. Ова е еквивалентно на отсуство на каква било анализа, бидејќи ако веќе имаме многу предупредувања, тогаш појавата на друга (колку и да е сериозна) при промена на кодот поминува незабележано.

Познати се следните методи за воведување квалитетни порти:

  • Поставување ограничување на вкупниот број предупредувања или бројот на предупредувања поделен со бројот на линии на код. Ова функционира лошо, бидејќи таквата порта слободно дозволува да поминат промени со нови дефекти, се додека нивната граница не е надмината.
  • Поправање, во одреден момент, на сите стари предупредувања во кодот како игнорирани и одбивање да се изгради кога ќе се појават нови предупредувања. Оваа функционалност е обезбедена од PVS-studio и некои онлајн ресурси, на пример, Codacy. Немав можност да работам во PVS-studio, што се однесува до моето искуство со Codacy, нивниот главен проблем е што одредувањето што е „стара“ и што е „нова“ грешка е прилично сложен алгоритам што не секогаш функционира. правилно, особено ако датотеките се многу модифицирани или преименувани. Според моето искуство, Codacy можеше да игнорира нови предупредувања во барањето за повлекување, а во исто време да не помине барање за повлекување поради предупредувања кои не беа поврзани со промени во кодот на даден ПР.
  • Според мене, најефективното решение е она што е опишано во книгата Континуирана испорака „метод на штракање“. Основната идеја е дека бројот на предупредувања за статичка анализа е својство на секое издание, а дозволени се само промени кои не го зголемуваат вкупниот број на предупредувања.

Рачка

Работи на овој начин:

  1. Во почетната фаза, во метаподатоците се прави запис за ослободување на бројот на предупредувања во кодот што го пронајдоа анализаторите. Значи, кога ќе изградите возводно, вашиот менаџер на складиште не пишува само „издание 7.0.2“, туку „објавување 7.0.2 што содржи 100500 предупредувања за стил на проверка“. Ако користите напреден менаџер на складиште (како што е Artifactory), складирањето на таквите метаподатоци за вашето издание е лесно.
  2. Сега секое барање за повлекување, кога е изградено, го споредува бројот на добиените предупредувања со бројот на предупредувања достапни во тековното издание. Ако ПР доведе до зголемување на овој број, тогаш кодот не ја поминува портата за квалитет за статичка анализа. Ако бројот на предупредувања се намали или не се промени, тогаш поминува.
  3. На следното издание, повторно пресметаниот број на предупредувања ќе се запише повторно во метаподатоците за објавување.

Така, малку по малку, но стабилно (како кога работи рачката), бројот на предупредувања ќе се стреми кон нула. Се разбира, системот може да се измами со воведување на ново предупредување, но коригирање на туѓо. Ова е нормално, бидејќи на долги растојанија дава резултати: предупредувањата се коригираат, по правило, не поединечно, туку во група од одреден тип одеднаш, и сите лесно отстранливи предупредувања се елиминираат доста брзо.

Овој графикон го прикажува вкупниот број на предупредувања во стилот на проверка за шест месеци од работењето на таквата „честалка“. еден од нашите проекти со отворен код. Бројот на предупредувања е намален за ред на големина, а тоа се случи природно, паралелно со развојот на производот!

Спроведување на статичка анализа во процесот, наместо да барате грешки со неа

Јас користам изменета верзија на овој метод, одделно броејќи ги предупредувањата по проектен модул и алатка за анализа, што резултира со датотека YAML со метаподатоци за изградба што изгледа вака:

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

Во секој напреден CI систем, ракет може да се имплементира за какви било алатки за статичка анализа без да се потпира на приклучоци и алатки од трети страни. Секој анализатор произведува свој извештај во едноставен текст или XML формат кој е лесен за анализа. Останува само да се напише потребната логика во CI скриптата. Можете да видите како ова се спроведува во нашите проекти со отворен код базирани на Џенкинс и Артифактори тука или тука. И двата примери зависат од библиотеката рачетлиб: метод countWarnings() брои xml ознаки во датотеките генерирани од Checkstyle и Spotbugs на вообичаен начин, и compareWarningMaps() го имплементира истиот престанок, фрлајќи грешка кога се зголемува бројот на предупредувања во која било од категориите.

Можна е интересна имплементација на „стаклата“ за анализа на правописот на коментарите, текстуалните буквали и документацијата со помош на аспел. Како што знаете, при проверка на правописот, не се точни сите зборови непознати за стандардниот речник, тие можат да се додадат во речникот на корисникот. Ако направите приспособен речник дел од изворниот код на проектот, тогаш портата за квалитет на правопис може да се формулира на вака: извршување аспел со стандарден и прилагоден речник не треба не најдете правописни грешки.

За важноста на фиксирање на верзијата на анализаторот

Како заклучок, поентата што треба да се забележи е дека без оглед на тоа како ја спроведувате анализата во вашиот цевковод за испорака, верзијата на анализаторот мора да биде фиксирана. Ако дозволите анализаторот спонтано да се ажурира, тогаш при составување на следното барање за повлекување, може да се „појават“ нови дефекти кои не се поврзани со промените на кодот, но се поврзани со фактот дека новиот анализатор едноставно може да пронајде повеќе дефекти - и ова ќе го прекине вашиот процес на прифаќање барања за повлекување. Надградбата на анализаторот треба да биде свесна акција. Сепак, ригидното фиксирање на верзијата на секоја компонента на склопот е генерално неопходен услов и тема за посебна дискусија.

Наоди

  • Статичката анализа нема да ви најде грешки и нема да го подобри квалитетот на вашиот производ како резултат на една апликација. Позитивен ефект врз квалитетот може да се постигне само преку неговата постојана употреба за време на процесот на испорака.
  • Наоѓањето грешки воопшто не е главната задача на анализата, огромното мнозинство на корисни функции се достапни во алатките со отворен код.
  • Спроведување на квалитетни порти врз основа на резултатите од статичката анализа во првата фаза од цевководот за испорака, со користење на „растелка“ за наследниот код.

референци

  1. Континуирана испорака
  2. А. Кудрјавцев: Анализа на програмата: како да разберете дека сте добар програмер извештај за различни методи на анализа на кодови (не само статички!)

Извор: www.habr.com

Купете доверлив хостинг за сајтови со DDoS заштита, VPS VDS сервери 🔥 Купете сигурен веб-хостинг со DDoS заштита, VPS VDS сервери | ProHoster