Jak snížit chybovost při vývoji aplikací

Přiblížím vám několik oblastí, na které byste se měli zaměřit, pokud nejste spokojeni s počtem chyb, které se vám dostávají na produkční server. Mým záměrem není vydat další článek typu „vyhněte se duplikaci“ nebo „pište testy“, protože takových je hromada, ale vyzdvihnout postupy, které tak hluboce zakořeněné ještě nejsou. Nejspíše i proto, že chybám můžete zabránit nejen technickými, ale i komunikačními dovednostmi.

Nejprve upřesním, co vůbec považuji za chybu. Nechci se pustit do nepopulárního akademického cvičení o co nejpřesnější definici, ale líbí se mi tvrzení, že chyba je jakákoli odchylka chování aplikace od toho, co uživatel očekává. Zastřešuje totiž nejen zjevné pády aplikací a pětistovky, ale i chybné operace s daty a jejich prezentaci, nekonzistence, neintuitivní rozhraní a utváření špatného mentálního modelu v hlavě uživatele. 1

Požadavky a zpětná vazba

Řada chyb se rodí ještě v době, kdy není napsaná jediná řádka kódu. Neměli byste začít programovat na základě nejednoznačného či neúplného zadání. Vyžádat si jeho upřesnění či opravu je levné, předělávat hotovou feature či celou aplikaci je drahé, časově náročné a nikoho nebaví.

I v případě, že je zadání v pořádku, je potřeba se neustále ujišťovat, že ho chápete správně a že se ho držíte. Pokud se jedná o vývoj většího celku, doporučuji zadavatele seznamovat s jeho stavem a podobou nejen na konci, ale i v průběhu. Jak často, to záleží na formě spolupráce. V případě interního vývoje klidně několikrát týdně, ve vztahu dodavatel-klient alespoň dvakrát měsíčně.

Některé změny svou povahou umožňují nasazení do produkce, i když nejsou ještě úplně hotové. Pokud např. předěláváte datový model aplikace, aby dokázal pojmout nějakou novou funkcionalitu, pro kterou budete uživatelské rozhraní vyvíjet v další fázi, nasaďte i takovou „neviditelnou“ změnu rovnou do produkce. Cílem je co nejrychlejší získání zpětné vazby – můžete ihned pozorovat vliv na zátěž serveru, přijdete brzy na případné problémy se začleňováním do zbytku systému a upozorníte zadavatele na hrozbu nevyhnutelného zpoždění. Podobně můžete nasadit a testovat třeba i sběr dat týdny či měsíce před tím, než tato data někdo v aplikaci uvidí. 2

Termíny

  • Klient: „Jak dlouho vám zabere vyvinout X?“
  • Vývojář: „Čtyři až pět týdnů.“
  • Klient: „My to ale potřebujeme za dva!“

Jak se obvykle zachováte v takové situaci? Smíříte se s tím, že budete muset pracovat šestnáct hodin denně namísto osmi? Slíbíte klientovi, že to bude za dva týdny, ale po jejich uplynutí mu řeknete, že to potrvá ještě další tři?

Stejně jako devět žen nedokáže porodit dítě za měsíc, stejně jako dvojnásobný počet vývojářů nezkrátí délku vývoje na půlku, tak ani vyřčené odhady nelze nijak magicky obejít. Uspěchaný produkt bude obsahovat více chyb.

Správné řešení je dohodnout se, zdali je důležitější dodržení vyžádaného termínu, nebo rozsahu zadání, a přijít s nějakým kompromisem, typicky kratším vývojem ořezané verze. Důležité je stát si za tím, že odhad původního rozsahu je neměnný.

Code reviews

Aplikace psaná jedním programátorem nikdy nebude tak kvalitní jako při vývoji ve více lidech. Ale z většího týmu těžíte jen v případě, že děláte code reviews. Veškerý kód, až na triviální bugfixy, by před začleněním do hlavní větve a nasazením měl vedle autora vidět ještě alespoň jeden vývojář.

Při zkoumání kódu kolegy se nejdříve seznámím se zadáním jeho úkolu a vymyslím, jak bych při jeho řešení asi postupoval a co všechno bych musel udělat. Zabývám se vhodností zvolených datových struktur, architekturou a názvy tříd, metod a proměnných. Z kódu musí být zřejmé, co dělá. Rozdíly mezi imaginárním a skutečným řešením pak s autorem konzultuji – někdy přijdeme na to, že je nedomyšlené to moje, někdy jeho.

Na každý kus kódu je potřeba se dívat velice kriticky a přemýšlet, jaká kombinace vstupních dat by ho mohla rozbít. Ovšem ego musí stranou – obě strany si musí uvědomit, že je pod drobnohledem kód a nikoli jeho autor. Kontrolor tedy nesmí napadat autora a autor si výtky vůči jeho vlastnímu kódu nesmí brát osobně. Někdy je těžké se rozloučit s tou metodou, na které jsme si dali tak záležet. Pokud ale nezapadá do širšího kontextu, musí jít pryč.

Součástí review by mělo být i důkladné otestování.

Verzování

To není jen zálohování. Verzování považuji za nedílnou součást kódu, sám jím při vývoji trávím přibližně třetinu času. Verzovací nástroj vám při správném používání usnadní týmovou spolupráci a pomůže s hledáním zdrojů chyb. Ten váš byste se měli naučit dokonale ovládat.

Dělejte malé atomické commity. Tedy takové, které se týkají pouze jednoho úkolu a nic nerozbijí. Cílem je zvýšit čitelnost diffů pro review a ulehčit případné vrácení těchto změn. 3

V případě, že používáte Git, naučte se používat příkazy reset a rebase (i jeho interaktivní variantu). Přepisování historie je důležité pro zamezení vzniku merge hell a navádí vás také k častému commitování, takže nepřijdete nedopatřením o svoji práci. Jelikož se ale časté commity nemusí vždy slučovat s atomičností a stabilitou, můžete těsně před odevzdáním pomocí uvedených příkazů historii uklidit, aby tato kritéria splňovala. 4

Stabilní commity vám umožní používat příkaz bisect. Ten využijete v momentě, kdy máte nějakou chybu, o které víte, že kdysi v systému nebyla, ale nedokážete dohledat, kde vzniká. git bisect pomocí binárního vyhledávání nalezne první commit, ve kterém se projevuje. Pokud ale máte v historii takové commity, ve kterých je aplikace značně rozbitá, máte toto hledání ztížené.

Automatizace

Pokud jakákoli často prováděná operace sestává z více netriviálních kroků, je velmi náchylná na lidské chyby. Proto by operace jako nasazování aplikace, spouštění migrací, kompilace javascriptu a CSS, mazání cache apod. měly mít podobu jediného příkazu. Můžete k tomu použít build nástroje jako Phing nebo Make. Kromě získání odolnosti proti chybám ušetříte i čas.

A když už je aplikace od nuly zprovoznitelná zavoláním jediného příkazu, nic nebrání její automatické kontrole na tzv. continuous integration serveru jako jsou Travis a Jenkins. Ty nad každým commitem v repozitáři mohou spouštět statickou analýzu, automatické testy nebo kontrolu dodržování coding standardu. Pokud píšete testy, ale nemáte zařízené jejich automatické spouštění a zasílání upozornění když selžou, tak jako kdybyste je neměli. Continuous integration nemá nahrazovat code reviews, ale doplňovat je. Můžete se tak při nich soustředit na věcné problémy a nezabývat se formátováním kódu, jehož správnost se dá ověřit automatizovaně.

  1. Za jedny z nejfatálnějších chyb považuji tzv. gulf of execution a gulf of evaluation. Tyto pojmy z teorie HCI vyjadřují dva nejčastější zdroje frustrace uživatelů: „Vím, co chci udělat, ale nevím jak“ a „Něco jsem udělal, ale nevím, jestli se opravdu stalo to, co jsem chtěl“.

  2. Crawling cizích webů, stahování dat z API či import e-mailů.

  3. Příkaz git revert, který jako parametr přijímá hash commitu k vrácení, vytvoří nový, přesně opačný commit. Tedy ty řádky, který původní commit přidával, budou odebrány, a odebrané řádky opět přidány. Proto se vyplatí dělat malé commity – větší obvykle takto snadno revertovat nejdou a je potřeba změny ručně „vyzobávat“.

  4. „With Git, the trick is to think of history less as ‚history‘ and more as a step-by-step description of your codebase.“ – Max Howell

The Curse of Smart People

Avery Pennarun, zaměstnanec Googlu, o nejednoduché práci s nejchytřejšími programátory planety:

Logic is a pretty powerful tool, but it only works if you give it good input. As the famous computer science maxim says, „garbage in, garbage out.“ If you know all the constraints and weights - with perfect precision - then you can use logic to find the perfect answer. But when you don‘t, which is always, there‘s a pretty good chance your logic will lead you very, very far astray.

Killing the Crunch Mode Antipattern

Přesčasy sice přináší krátkodobý užitek, ale dlouhodobě nefungují a dokonce škodí.

The feeling of finishing tons of work in a short period and depriving oneself of quality personal time can be addicting, especially when it results in „saving the day“ for a project. Rolling up your sleeves and cranking to the end of a deadline makes you feel valuable in a very concrete way. Without your overtime, the project doesn’t get done on time. With it, the project is saved. It’s hard to find such black and white ways to add value in daily „normal“ work.

Doctrine 2 není pomalá!*

*pokud ji umíte používat a správně si ji nakonfigurujete

Rozmáhá se nám tu takový nešvar. Čas od času se objeví někdo s tvrzením, že Doctrine je pomalá a tudíž nepoužitelná na reálných projektech. Tento omyl plyne z několika mýtů, kterými je Doctrine 2 opředená.

Jak už jsem psal před třemi a půl lety, hlavní motivace pro používání ORM je reprezentace dat z databáze v aplikaci prostřednictvím konzistentních objektů. Objektů, které nejsou obecné univerzální hashmapy s neznámým obsahem, ale objektů, které mají pevné dané rozhraní a tudíž vím, jaká data z nich mohu získat a jak s nimi manipulovat. Po ORM tedy nechci, aby mi ušetřilo psaní SQL dotazů a nechci ani odstínit od konkrétní databáze, kterou používám. Mohu si tedy dovolit optimalizovat výběr dat velmi podobně, jako s jinými „lightweight“ knihovnami, které podle výše odkazovaných zlých jazyků již pomalé nejsou.

Pokud tedy chci výkonnou aplikaci, nenechávám většinu SQL dotazů generovat Doctrine, ale píšu je sám za pomoci DQL, což je příbuzný jazyk, který Doctrine používá. Namísto tabulek sloupců se v něm odkazujete na entitní třídy a atributy. Je snadno rozšiřitelný, což se hodí, pokud potřebujete využít nějakou vlastnost vaší databáze, kterou DQL v základu nepodporuje.

V případě, že chci z databáze vybrat data pouze pro čtení (typicky pro výpis v šabloně), není třeba přenášet celé objekty. V takovém případě v DQL vyjmenuji sloupce, které mě zajímají a Doctrine mi výsledek dotazu vrátí v obyčejných polích. Díky DQL se dá vyhnout i 1+N problému (lazy loading uvnitř cyklu). Avšak od určité návštěvnosti si nemůžete dovolit stále sahat na čerstvá data z databáze s žádným nástrojem a musíte nasadit aplikační cachování tak jako tak.

Odpůrci Doctrine často argumentují tím, že je to moloch. Nevím, co tím přesně myslí. Asi se jim nelíbí, že jsou její zdrojáky v mnoha souborech, a je jim daleko sympatičtější přístup mPDF. V každém případě ale velikost knihovny nemá dopad na výkon, pokud si zapnete a správně nakonfigurujete OpCache, která výrazně ulehčí práci PHP.

Dále byste si na produkci měli vypnout automatické generování proxy tříd:

$config->setAutoGenera­teProxyClasses(FAL­SE);

Doctrine jinak totiž bude při každém požadavku pro každou entitu generovat proxy, což je, ano, pomalé.1

Také je třeba nastavit cache pro metadata (konfigurace entit) a zkompilované DQL dotazy. Viz výpis všech dostupných driverů.

$cache = new \Doctrine\Com­mon\Cache\ApcCache();
$config->setMetadataCache­Impl($cache);
$config->setQueryCache­Impl($cache);

Správnost těchto nastavení vám ověří CLI command orm:ensure-production-settings.

Nejhorší nápad, jaký můžete dostat, je psát si vlastní ORM. Jedná se o neuvěřitelně komplexní a náročnou oblast. Problémy, které budete řešit, za vás již dávno vyřešili autoři Doctrine. Pokud ale máte dva roky čas na full-time vyvíjet vlastní ORM a myslíte si, že to zvládnete líp než oni, nebudu vám bránit.

  1. Proxy třídy slouží k lazy loadingu navázaných dat – Doctrine vygeneruje kód, který při přístupu k nenačtené proměnné položí dotaz do databáze.

Deprese softwarových vývojářů

Fascinující seriál popisující několik příběhů ze života v malých i velkých firmách. Ačkoli popisované překážky a problémy většina z nás nebere tak vážně, s jejich určitou variací se setkal snad každý.

Tablety, PC a Office

Benedict Evans o tom, jak jsou obecné nástroje typu Office postupně nahrazovány jednoúčelovými, ale efektivními aplikacemi, čímž klesá i potřeba klasických počítačů s klávesnicí a myší:

When I worked at Orange there was a multi-megabyte Excel file on the network drive called, I believe, ‘sum_of_x.xls’ containing complex macros and every major operating metric for the entire company, there for anyone who needed to analyze high-level data. That should probably not, really, be in Excel today.

The same applies to Powerpoint - it’s a very good tool for that 150 slide deck, but what if you’re making a 10-slide deck each week that consists entirely of operating metrics pulled out of a back-end system, manipulated in Excel and pasted into slides, plus commentary, that are emailed to 25 people? Shouldn’t that change from a 2 hour task to a SAAS dashboard and a 30-second task? And would it still need a mouse and keyboard?

Jak se webař stal mobilním vývojářem

Na své profesní dráze jsem si prošel několika fázemi. S programováním jsem začal naklikáváním a psaním jednoduchých okenních aplikací v C++Builderu. Pak přišlo připojení na Internet a já upřel svoji pozornost na web. Naučil jsem se HTML a vyzkoušel slepou větev v podobě Microsoft FrontPage. Dlouhou dobu jsem si s ním vystačil. Okolo roku 2005 jsem se začal učit PHP a vydělával si pravidelně pár tisíc korun malými zakázkami. Zároveň se začátkem studia na ČVUT FEL jsem se naučil Nette a ve druhém ročníku se přihlásil na inzerát do firmy, kde jsem zažil a stále zažívám největší posuny ve svých znalostech a dovednostech.

Moje pokroky v programování za posledních 15 let vždycky pramenily z toho, že jsem ostatním záviděl jejich výtvory a chtěl mít vlastní. Ať už šlo o zkompilované .exe soubory, webové stránky, redakční systémy, rozšíření do Nette – prostě všechno, co bylo v dané době středem mého zájmu.

Už tři a půl roku mám iPhone. Za tu dobu jsem si ke všem těm ikonkám na homescreen vytvořil vřelý vztah. Zblízka sleduju novinky o Applu a vím o všem, co se v iOS a Mac komunitě šustne. Vytříbil jsem si díky tomu smysl pro kvalitu a user experience, který se snažím aplikovat i při tvorbě webů.

Ale to mi samozřejmě nestačí. Potřeboval jsem zjistit, jak mobilní vývojáři dělají mé oblíbené aplikace. Nainstaloval jsem Xcode a začal se učit z přednášek ze Stanfordu. Napsal jsem si pár jednoduchých aplikací, abych pochopil probírané principy.

Mobilní vývoj je něco úplně jiného než web – nestačí odpovědět na bezstavový HTTP požadavek a umřít. Aplikace může běžet třeba i desítky minut a musí se chovat správně při libovolných kombinacích uživatelských vstupů. Náročné výpočty je třeba odsouvat do vedlejších vláken, protože uživatelské rozhraní musí být stále plynulé. Vše běží o hodně blíže železu a občas je to na nízkoúrovňovosti kódu poznat. Celé je to pořádná výzva a ty já rád.

Uživatel na telefonu nemá k dispozici klávesnici a myš, takže si neporadí s pekelně naprogramovanými formuláři, které jsou na webu běžné, ale veškerou funkcionalitu je potřeba detailně promyslet a připravit mu ji na zlatém podnose, ať svého cíle dosáhne co nejjednodušeji. Zatímco na webu vám projde, že za den vychrlíte čtyři formuláře, u kterých stačí, že se budou chovat nějak takhle, na iOS můžete strávit tvorbou jedné obrazovky třeba měsíc a nikdo se tomu nebude divit.

Po šesti měsících vývoje dnes vydávám svou první aplikaci, kterou jsem vyvinul společně s Michalem Langmajerem – jsme na ni pořádně pyšní. Pokud máte guláš v tom, kterým přátelům dlužíte a kdo dluží vám, určitě ji omrkněte.

SettleApp

Mobilní vývoj je pro mě zatím jen hobby na dlouhé zimní i letní večery, ale možná se z toho jednoho dne stane seriózní obživa. Držte mi palce!

Strukturovaná prokrastinace

Skutečně fungující lifehack, jak dosáhnout vysoké produktivity. Přišel jsem si na něj už dávno sám, ale je super ho vidět popsaný černý na bílém.

Ve zkratce jde o to, že pokud potřebujete udělat úkol X, najdete si zdánlivě daleko důležitější úkol Y, kterému se vyhýbáte a jehož prokrastinací dosáhnete dokončení úkolu X. Pokud poté potřebujete udělat Y, najdete si k vyhýbání urgentnější úkol Z.