sobota 14. září 2013

Život s debuggerem

… aneb debugování jako indikátor problémových postupů

Byl pátek třináctého, navíc 256. den v roce - svátek programátorů a já jej oslavil téměř 10-tihodinovou programovací šichtou. Po celý den jsem se pokoušel striktně dodržovat TDD přístup. Padající test, nejmenší nutná implementace, zelenáč. A takhle pořád dokola. Jako když rytmicky dýcháte - nádech a výdech. Práce šla od ruky a unit testy rodily produkční kód pěkně podle plánu. Za celý den jsem použil debugger snad jen dvakrát. V kontextu událostí jsem si utvítnul trochu provokativní tvít:

Akce rodí reakci a ta také přišla. Twitter je plný šikovných lidí se spoustou zkušeností z praxe a já jsem za jejich reakce moc rád. Zároveň je Twitter omezený na 140 znaků a to se pak těžko argumentuje. Dejte mi šanci utvítnutou myšlenku trochu vysvětlit formou tohoto příspěvku.

Dopředu je potřeba přiznat, že bych se bez možnosti debuggování aplikace určitě neobešel. Na druhou stranu pozoruju, že s postupným profesním vývojem se u mě nutnost používání tohoto nástroje postupně snižuje. Snažím se hledat postupy, které vnášejí do vývoje aplikace větší jistotu a menší chybovost.

Unit testy a TDD

K jedné z největších revolucí v mých pracovních návycích došlo v okamžiku, kdy jsem začal psát unit testy. Potenciál unit testů využijeme naplno až v kombinaci s TDD nebo nějakou jinou test-first technikou. Ta nás nutí přemýšlet dopředu o struktuře a funkcionalitě kódu. Rozkládat komplexnost řešení na malé samostatné scénáře použití. Jedním z klíčových aspektů je postupná, iterativně přidávaná funkcionalita. I při budování rozsáhlé aplikace metodou malých přírůstků je pravděpodobné, že budete mít stav věcí pod neustálou kontrolou. Určitě mnohem více, než když nakódujete desítky řádek kódu a pak se je snažíte najednou otestovat a integrovat.

Krůčky jsou tak elementární, že téměř ani není nutné spouštěný test debugovat. Třídy jsou testovány v izolaci a pokud navíc dodržujeme např. SOLID, rozpadá se nám funkcionalita do malých tříd s jasně definovanými zodpovědnostmi. Takové třídy se pak dobře testují. Práce bez debuggeru je mnohem rychlejší.

Integrační testování

Jistota z unit testů se přenáší do integračních testů, které jsou cílené na ověření spolupráce mezi více reálnými třídami. V případě dobře navržených rozhraní a podpory IoC kontejneru je celý proces integrace, opět po postupných krůčcích, transparentní a méně náchylný k chybám. Máme odzkoušené jednotlivé třídy? Pak zřejmě bude fungovat i jejich kompozice.

Při integraci se do akce dostávají také frameworky třetích stran. Míra nutnosti použití debuggeru pak bude zřejmě odvislá od naší znalosti jejich API a také od toho, jak dobře použitelné tyto frameworky jsou. Nerozumím používanému frameworku? Asi si hodně užiju debugování.

Čistý, přehledný a jednoduchý kód

Programátor dospívá v okamžiku, kdy se mu přestává líbit složitý kód a začíná hledat prostředky zajišťující jednoduchost. Skrze kód komunikujeme s počítačem a s ostatními členy týmu. A je to podobné jako jiné formy komunikace. V případě, že něčemu skutečně rozumíme, umíme to vyjádřit jednoduše a jasně. Dobré komunikační dovednosti posouvají programátora na vyšší úroveň a zvyšují jeho hodnotu pro týmovou spolupráci.

Pokud nezměníme firmu nebo náš projekt nekrachne, pak nás vlastní kód bude pronásledovat ještě dlouhé roky. Určitě znáte ten pocit, kdy se po delší době vrátíte ke staršímu kódu a nerozumíte mu. Dokonce se k němu někdy nechceme ani znát. To jsem opravdu napsal já? No nic, nezbývá než refaktorovat.

Že se nám podařilo napsat nesrozumitelný kód poznáme podle toho, že jsme nuceni často spouštět debugger a krokovat, abychom pochopili sémantiku kódu.

Rychlost nebo kvalita

Vždy preferujme dobrý návrh a udržovatelnost kódu nad rychlostí jeho vytváření. U dlouhodobých projektů se nám to vrátí mnohonásobně.

Někde na fóru jsem četl myšlenku:

Jednou z nejdůležitějších dovedností programátora je schopnost psát jednoduché kousky kódu správně napoprvé, bez nutnosti používat debugger.

Tato myšlenka nám dává návod, jak být současně rychlí. Naučit se malé elementární programátorské problémy řešit napoprvé správně a kompozicí z nich budovat složitější. Získat jistotu v základech naší práce - v algoritmizaci.

Diagnostika

Dobře zvolená diagnostika aplikace nám může ušetřit časově náročné ladění aplikace skrze debugger. Logování chování aplikace a především podrobný kontext chyby je neocenitelným pomocníkem pro rychlou navigaci k problémovému místu. Nespoléhejme se na důvěryhodnost hlášení od uživatelů, zajistěme si automatizovaně dostatek informací k problémům sami.

Techniky na detekci a předcházení chybám

Debugger je deratizérský nástroj určený pro odchyt otravných, obvykle v pátek odpoledne se objevujících, bugů. Ještě před použitím vlastního ladění nebo ihned poté co jsme chybu přes debugger detekovali, můžeme zkusit chybu izolovat. Na chybový scénář napíšeme jednotkový nebo integrační test. Fixnutí bugu tak odpovídá stavu, kdy nám začne procházet nově napsaný test.

Mnohem levnější, než nahánět bugy nahlášené od testerů a uživatelů, je bugům aktivně předcházet. Výše popsané techniky můžeme doplnit o revize kódu nebo přímo programovat v páru a tím code review dělat v reálném čase. Důležité je samozřejmě používat jednotné kódovací standardy a jmenné konvence, které urychlují orientaci a porozumění kódu všem členům týmu a snižují riziko špatného použití. Využívejme nástroje na statickou analýzu kódu, které umí najít spoustu potenciálně rizikových míst. Neignorujme kompilační warningy. Jsou to tiše tikající časované bomby, které se mohou změnit v nepříjemné chyby. Praktikujme společné vlastnictví kódu, které pomáhá šířit znalosti v týmu a kontinuálně vylepšuje code base. Využívejme průběžnou integraci na buildovacím serveru. Zlepšujme komunikaci na všech úrovních struktury projektového týmu.

Závěrem

Debugger jako nástroj má samozřejmě své opodstatnění. Ve vlastním zájmu bychom se ho měli naučit ovládat efektivně a znát všechny jeho možnosti. Zároveň je však třeba si uvědomit, že jeho časté nadužívání může indikovat zásadnější problémy v našich postupech a dovednostech. Vystupme občas z pracovního stereotypu a přemýšlejme nad tím, jak naši práci zefektivnit tak, abychom debugger - nástroj poslední záchrany - nemuseli používat příliš často.

4 komentáře:

  1. Já jsem byl na Twitteru ten, co se snažil polemizovat. S podstatou článku souhlasím.

    Je pravda, že debugger je nástroj pro zjištění stavu věcí, když nevíme v čem je problém a to by tak často být nemělo.

    Když jsem se nad tím zamyslel, tak nejčastěji používám debugger při zkoumání problémů v knihovnách 3 stran nebo když se snažím rozkrýt logiku jejich fungování (vše v dokumentaci nenajdete).

    Nebo k nalezení problému, který mi reportuje někdo jiný - typicky to znamená, že kód při se při konkrétních vstupech nechová, jak bylo zamýšleno. Tj. při tvorbě obvykle integračních testů mě ani ve snu nenapadlo, že daná situace může nastat a že v ní ty kolečka nespolupracují tak, jak mají.

    No a v poslední řadě při analýze problémů v testech - třeba když po refaktoringu některé testy nechodí a já už zapomněl jak vypadá ta logika pod nimi, nebo tu logiku psal někdo jiný.

    Je to docela zajímavé, protože jsem se nikdy moc nezamýšlel, v jakých situacích debugger používám. Zkusím se na to teď víc soustředit a ptát se, jestli to ta daná situace skutečně vyžaduje nebo je problém jinde :)

    OdpovědětSmazat
    Odpovědi
    1. Honzo, díky moc za komentář a taky za tvou reakci na Twitteru, která mě motivovala k napsání blogpostu po více jak půl roce. ;)
      Ten tvít byl opravdu trochu kontroverzní a jeho účelem bylo vynutit krátké zamyšlení nad užíváním tak běžného nástroje jakým debugger je. To se v případě nás dvou snad podařilo. Měj se fajn.

      Smazat
  2. Debugger má svůj význam - ale jakmile ho vidím jindy, než ve fázi "zkoumáme jak funguje knihovna třetí strany", biju svoje ovečky krumpáčem přes ruce.
    Jakmile si programátor zvykne, že problémy se dají řešit pomocí debuggeru, zapomíná logovat či jinak provádět diagnostiku. A v produkčním prostředí u zákazníka si debugger na serveru spustit nelze. Často se člověk ani nedostane na konzoli serveru - a jediné, co může, je poprosit o zaslání logů.
    Takže se snažím všechny naučit, že aplikace musí logovat natolik detailně, aby se z logů dalo usoudit, co se kdy stalo a proč.
    Občas to bolí.

    OdpovědětSmazat