Uvod u debagovanje
Možda se čini kao kliše, ali kad Vikipedija ima savršenu definiciju debagovanja, stvarno nema smisla izmišljati toplu vodu – Debagovanje je proces pronalaženja i rešavanja bagova ili nedostataka koji sprečavaju ispravno izvršavanje računarskog softvera ili sistema.
Sam pojam debagovanje je nastao 1940-ih kada se moljac zaglavio u releju Mark II kompjutera na Harvardu dok je admiral Grejs Hoper radila na njemu.
Prvi kompjuterski bag je stvarno bio buba (eng. bug).
Bitno je istaći da je debagovanje veština koju moramo da naučimo i na kojoj moramo da radimo ako želimo da postanemo dobri u tome. Početnici u softver developmentu bi trebalo da posvete vreme upoznavanju sa osnovama debagovanja kako bi izbegli npotreban stres kada se pojavi neočekivano ponašanje aplikacija na kojima rade.
Inače, takvo ponašanje će se pojaviti. To je jedna od onih retkih neminovnosti razvoja softvera.
U ostatku ovog članka, posvetićemo pažnju tehnikama i pristupima debagovanja veb aplikacija (mada se većina može primeniti na bilo kakvo debagovanje), koristeći JavaScript i PHP kao primere programskih jezika (same tehnike ne zavise od jezika).
Koraci u debagovanju
Reprodukcija baga
Prvo što radimo kada pokušavamo da popravimo prijavljeni bag je da probamo da ga reprodukujemo. Ovo bi uvek trebalo da bude prvi korak. Da bismo ovo uradili, možemo konsultovati osobu koja je prijavila bag kako bismo dobili instrukcije za reprodukciju. Pomoću reprodukcije izbegavamo jurnjavu za nepostojećim bagovima. Takođe, reprodukcija nam omogućava da kasnije validiramo popravku koju smo izvršili.
Naravno, postoje situacije u kojima ne možemo reprodukovati bag iako je prisutan error log. Ako zaključimo da je greška prisutna i da predstavlja opasnost po aplikaciju, možemo pokušati da uhvatimo grešku u kodu (koristeći try/catch) i da dobijemo više detalja o grešci kako bismo otkrili koje ponašanje u stvari dovodi do baga.
U slučajevima gde smo potpuno sigurni da sama greška ne predstavlja nikakvu opasnost po sistem, bagu možemo pristupiti defanzivno, eliminišući prijavljivanje te iste greške u budućnosti.
Analiza stek trejsa
Pri prolasku kroz error report, proveravamo da li je prisutan stack trace. U stack trace-u, pokušavamo da pronađemo poslednji poziv u kodu zaduženom za poslovnu logiku, pošto se problem u većini slučajeva javlja upravo ovde. Ako hvatamo i ponovo bacamo izuzetke u kodu, treba da pokušamo da ih kastomizujemo i podesimo odgovarajuće poruke o izuzetcima kako bi bile lakše dostupne kasnije.
Primer stack trace-a u Laravelu
Problem se može javiti i kada stack trace nije prisutan tamo gde se greška javila. Tada je preporučljivo proveriti da li glavni error log-ovi sadrže podatke koji imaju veze sa greškom.
Takođe, ako nikakvi error log-ovi ne postoje, ovo može biti znak da nešto nije u redu sa mehanizmom za prijavljivanje grešaka u aplikaciji i rešavanje ovoga bi trebalo da bude prioritet. U slučajevima gde se greška javlja u odgovoru na HTTP zahtev, HTTP status kod može pružiti dovoljno informacija potrebnih da se pronađe greška.
Ako koristimo TDD/BDD – Ovo je takođe pravo vreme da napišemo test case za detektovani bag (u slučaju da pišemo automatizovane testove). Ovo će osigurati da se bag ne pojavi ponovo u budućnosti usled promena u kodu.
Konsultacija sa ostatkom development tima
Kada smo pronašli stack trace, prelazimo na rešavanje problema. Ako nam je stack trace nerazumljiv, konsultujemo se sa osobom koja je poslednja radila na tom delu koda da saznamo da li oni prepoznaju problem ili da li znaju gde se potencijalni problem nalazi.
Ukoliko ovo ne pomogne (ili ako je ta osoba nedostupna), konsultujemo se sa ostatkom tima da vidimo da li neko prepoznaje problem. U većini slučajeva, neko će imati neku ideju gde potražiti rešenje.
Pretraga na internetu
Ako nismo u stanju da pronađemo uzrok greške, možemo jednostavno potražiti na internetu uzrok greške u stack trace-u. Sajtovi kao što je Stack Overflow su prepuni odgovora koji će ili rešiti problem ili nas uputiti u pravom smeru. Ukoliko ne možemo pronaći grešku u biznis logici u stek trejsu, možemo koristiti Gugl da pronađemo greške vezane za kod biblioteka i frejmvorka koje se ne čine generičkim i koje se čine slične ponašanju aplikacije sa kojim imamo posla.
“Programiranje sa patkicom” i/ili programiranje u paru
Ukoliko ništa od gorenavedenog ne upali, možemo pokušati sa “programiranjem sa patkicom” ili programiranjem u paru. “Programiranje sa patkicom” podrazumeva objašnjavanje koda koji imamo pred sobom naglas (možemo koristiti gumenu patkicu kao sagovornika ili pitati kolegu). Detaljno objašnjavanje problema i toka aplikacije često vodi ka jednostavnom rešenju samog problema.
Ako objašnjavanje ne urodi plodom, možemo sesti sa kolegom i proći kroz kod zajedno. Pošto su dve glave pametnije od jedne, ova praksa najčešće ubrzava proces ispravljanja greške.
Tehnike uklanjanja baga
U sklopu koraka koje smo prethodno opisali, možemo preduzeti određene postupke koji će povećati šanse za uspešno rešenje problema.
Log i stop tehnika
Najbolji način da razumemo šta se dešava sa kodom je da pratimo tok aplikacije, logujemo stanje aplikacije na određenim mestima (komandama var_dump ili console.log) i stopiramo daljnju egzekuciju koda (komandama die ili alert/debugger). U određenim slučajevima moramo da pustimo daljnji tok aplikacije i u takvim situacijama ćemo preskočiti ‘stop’ deo ove tehnike.
Koristeći ovu tehniku, moći ćemo da validiramo da se tok aplikacije stvarno izvršava, da proverimo da li se stvarni podaci nalaze u tom stanju (npr. Neki korisnički input) i da analiziramo naš pokušaj ispravke na tom mestu.
Primer log i stop tehnike
Najveći problem pri korišćenju ove tehnike je postojanje race condition-a u kodu (više tokova aplikacije koji utiču na isto stanje aplikacije, često slučaj sa JS-om). Ako nismo sigurni da jedan tok aplikacije utiče na drugi i sumnjamo na postojanje race condition-a, treba logovati sve tokove kako bismo otkrili tačan redosled egzekucije.
Tehnika uklanjanja koda
Ova tehnika je korisna u slučajevima gde ne postoji stack trace (ili gde je on potpuno beskorisan).
Pre pribegavanja ovoj tehnici, moramo biti sigurni da promene na nekom drugom nivou aplikacije nisu prouzrokovale problem (promene DB config-a, promene server config-a, itd.) i da je kod poslovne logike odgovoran za javljanje baga.
Sledeće što radimo je da pregledamo promene u kodu kako bismo ušli u trag fajlu koji je odgovoran za bag. Ako nismo sigurni, krećemo od samog početka (npr. koda koji prima input od korisnika) i usput zakomentarišemo ili brišemo delove koda i koristimo log i stop tehnike nakon zakomentarisanog koda da utvrdimo da li je greška otklonjena.
Ako postoji velika količina koda na istom nivou, možemo koristiti tehniku polovljenja gde uklanjamo polovinu koda i proveravamo da li je bag otklonjen. Ako nije, to znači da je bag u drugoj polovini. Dalje primenjujemo istu tehniku kako bi smo saterali bag u ćošak i precizno ga locirali.
Bitno je napomenuti da postoji određeni slučaj kada je teško primeniti ovu tehniku i u tom slučaju imamo silent fail aplikacije koji se dešava nakon što je dodata velika količina koda (npr. nakon merge-a grane na git-u). U takvoj situaciji, najbitnije je prvo odrediti tačno koji komit je uveo problem. Ako se to ne može uraditi prostim pregledom koda (previše komitova, ili nam se nijedan komit ne čini sumnjivim), koristimo tehniku polovljenja u kombijnaciji sa git reset HEAD-om gde idemo unazad određeni broj komitova i proveravamo da li se bag još uvek javlja. Ako se bag još uvek javlja, idemo dalje unazad po git logu. Ako se ne javlja, polovimo broj komitova od poslednjeg proverenog komita koji još uvek sadrži bag do onog koji ga ne sadrži dok ne utvrdimo koji komit je uveo problem.
Nakon što je problematični komit identifikovan, primenjujemo standardne tehnike uklanjanja koda.
Korišćenje debagera za specifične tehnologije
Ako nam je potreban dodatni uvid i ako gorepomenute tehnike nisu odradile posao, možemo instalirati debagere za specifične tehnologije, kao što su Xcode za PHP, vue-devtools za VueJS, i slične.
Dalji koraci
Nakon što smo uspešno otklonili bag, prvo što pada na pamet je slavlje, naravno. Ipak, nakon slavlja bi trebalo uraditi još par stvari koje se mogu pokazati veoma korisnim u budućnosti. Trebalo bi podeliti nova saznanja o uzrocima baga sa kolegama i napisati test za ovakve slučajeve (ako koristimo automatizirane testove). Ako ništa drugo, onda bi trebalo negde zapisati test case običnim jezikom tako da bude dostupan timu ubuduće kako bi se lakše rešili slični problemi.
Ove male stvari mogu biti od ogromne koristi za dalje održavanje koda.