What is Code?

Naprosto epický článek extrémní délky (38 tisíc slov) se spoustou animovaných ilustrací, který popisuje vše okolo vývoje software, co potřebujete vědět. Od pochopitelného popisu, jak funguje procesor a co všechno se musí stát, aby se znak stisknutý na klávesnici zobrazil na obrazovce, přes programovací jazyky, knihovny, algoritmy, debugging, datové struktury, databáze a verzování, až po vývojářské konference, flamewary a management. Pokud zvažujete nebo již máte kariéru v softwarovém vývoji, tohle je pro vás skutečně must-read.

Začněte monolitem

Martin Fowler reaguje na aktuální módu mikroservisní architektury tvrzením, že začínat vývoj nové aplikace od mikroservis je nebezpečné, protože dopředu nevíte, jak do nich projekt rozdělit, a spíše si špatně aplikovanou architekturou uškodíte.

Any refactoring of functionality between services is much harder than it is in a monolith. But even experienced architects working in familiar domains have great difficulty getting boundaries right at the beginning. By building a monolith first, you can figure out what the right boundaries are, before a microservices design brushes a layer of treacle over them.

Mikroservisy jsou sice výhodnější z dlouhodobého hlediska, protože lze vývoj projektu rozdělit a škálovat do více nezávislých týmů, ale zároveň s sebou nese i nevyhnutelnou režii při navrhování a implementaci API, reagování na selhávající požadavky, nemožnost stahovat si všechna data z jedné databáze naráz apod. A pokud vyvíjíte něco nového, nevíte, zdali daný projekt bude mít takový úspěch a růst, aby se mikroservisy a pomalejší příchod na trh vyplatily.

When you begin a new application, how sure are you that it will be useful to your users? It may be hard to scale a poorly designed but successful software system, but that‘s still a better place to be than its inverse. As we‘re now recognizing, often the best way to find out if a software idea is useful is to build a simplistic version of it and see how well it works out. During this first phase you need to prioritize speed (and thus cycle time for feedback), so the premium of microservices is a drag you should do without.

Objektivní srovnávání technologií

David Grudl na Twitteru vyzval vývojáře ke srovnání vývoje webové aplikace v Nette oproti řešení v JavaScriptu. Ať už by v rychlosti vyhrála jakákoli ze soutěžících technologií, zastávám názor, že to nic nevypovídá o její vhodnosti pro dlouhodobý vývoj a údržbu seriózních aplikací.

Tato srovnání mají mnoho společného se syntetickými benchmarky. To, co se v nich testuje, neodpovídá tomu, co v běžném provozu aplikace provádí, a zároveň hrubý výkon frameworku není to hlavní kritérium, které by vývojáře při výběru mělo zajímat.

Při vývoji není důležité, za jak dlouho dokáže vývojář na zelené louce nabušit první verzi aplikace podle pevného zadání, ale řada jiných kritérií:

  • Rozšiřitelnost a udržovatelnost aplikace, jak snadné je ve zdrojovém kódu provádět změny
  • Testovatelnost a pokrytí testy
  • Stav a předatelnost zdrojových kódů jinému vývojáři či týmu
  • Přívětivost uživatelského rozhraní
  • Dostupnost a cena vývojářů se znalostí technologie
  • Dokumentace, komunita a budoucnost technologie

Sám jsem se před třemi lety účastnil Souboje frameworků. Úkolem bylo, podobně jako v Davidově výzvě, za jeden den vyvinout e-shop s administrací. Každý ze soutěžících k úkolu přistoupil jinak a výsledné pořadí ne nutně odpovídalo tomu, jak by si daná aplikace vedla dlouhodobě. Např. vítězný Jakub Vrána ke tvorbě administrace použil svůj Adminer Editor, což mu v soutěži ušetřilo spoustu času, ale generovaná administrace na základě definice tabulek v relační databázi neposkytuje žádný prostor pro customizaci chování a tudíž přináší nižší uživatelský komfort.

Do srovnání samozřejmě promlouvá i zkušenost jednotlivých účastníků – a to jak celkově s programováním, tak s konkrétní soutěžní technologií.

Jak tedy postupovat při výběru technologie, když ji nám nepomůže vybrat jednodenní hackaton? Když jsme na konci loňského srpna začínali s vývojem nového nákupního košíku na Slevomatu, věděli jsme, že chceme, aby celý fungoval jako single-page aplikace a veškerá komunikace se serverem probíhala pomocí AJAXu. Potřebovali jsme technologii, která by do šablony na straně klienta promítala stav dat na serveru. Jako vhodné kandidáty jsme vybrali React, Knockout a šablonovací engine Handlebars. Strávil jsem tři dny porovnáváním těchto technologií tak, že jsem v každé naimplementoval první krok košíku, a posléze porovnával, která technologie je nejbližší tomu, jak ve firmě vývoj probíhá, odhadoval, s čím by mohly být v budoucnu problémy a jak snadno nám to či ono pomůže řešit. Vybral jsem Knockout a košík jsme o dva měsíce později úspěšně spustili bez nějakých větších problémů na klientu ani na serveru.

Vybral jsme tedy správně? Zádrhel spočívá v tom, že to nelze určit. Jak by vývoj probíhal v případě, kdybych zvolil React? Možná by byl košík hotový o dva týdny dříve, ale po spuštění bychom přišli na nepříjemný a těžko odladitelný bug. Možná bychom termín nestihli. Možná by byl při překreslování DOMu výkonnější, ale kodér by nedokázal upravovat jeho šablony. Pokud by nás opravdu zajímalo, zdali jsme vybrali dobře, mohli bychom se pokusit celé dva měsíce vyvíjet paralelně v obou technologiích zároveň, což by jednak stálo více peněz, a druhak bychom opět narazili na různou úroveň zkušeností vývojářů, srovnání by tedy opět nebylo objektivní.

Pokud by mě po dokončení košíku v Knockoutu zajímalo, jak by si vedl React, a pustil se do implementace v něm sám, opět bych nedokázal technologie srovnat objektivně, protože jsem při vývoji první verze nabral zkušenosti, které bych v Reactu aplikoval a dokázal se tak vyhnout některým slepým uličkám. Spravedlivé srovnání tedy nejenže nelze získat od více různých vývojářů, ale ani od jednoho.

Při výběru technologie berte vpotaz, jaká od ní máte očekávání a zdali je dokáže splnit, dovednosti a zkušenosti lidí, kteří s ní budou pracovat, a moc se netrapte úvahami, jak by projekt mohl dopadnout, pokud byste se na začátku rozhodli jinak. 1

  1. A určitě nehleďte na výkonnostní benchmarky, ankety popularity a co používá vaše oblíbená osobnost.

Výzkum, vývoj, výroba

Jirka Knesl rozděluje vývoj software na tři druhy:

Když bádáte, zkoušíte něco nového, něco, co na světě dosud není, výsledkem může být patent, nový objev, technologie. Když vyvíjíte, děláte pro zákazníka něco nového, ale obvykle používáte postupy, které jste už dříve znali. Přestože neohýbáte hotové řešení, musí něco vzniknout. A když vyrábíte, je od začátku do konce jasné, jaké kroky uděláte, činnost je předvidatelná a naprosto jasná, typicky se může jednat třeba o tvorbu jednoduchého webíku, ke kterému máte design i obsahy a víte, že máte nasadit Wordpress s pluginy X, Y, Z.

Nejnebezpečnější slovo při vývoji software

Prostě tam přidej tlačítko.

Prostě to napoj na API.

Prostě to někam nahraj.

Použití „prostě“ předpokládá, že nad daným úkolem není potřeba přemýšlet, a jediné, co zbývá, je ho naimplementovat. Každý zásah do systému ovšem může přinést nějaké problémy – pro uživatele, udržovatelnost, nebo výkon. Zamyslete se nad tím, jestli je vůbec třeba ho implementovat, nebo jestli nejde vyřešit nějak elegantněji.

Naučte své klienty, nadřízené a kolegy, že nic není tak jednoduché, a do všeho je potřeba vložit víc, než jen údery do klávesnice při psaní kódu.

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.