pondělí 23. září 2013

Snižování rizikovosti softwarových projektů

… aneb díky zpětné vazbě rychleji kupředu

Děláme to dobře? Bude se zákazníkovi a uživatelům výsledek líbit? Máme hodně nebo málo bugů? Stíháme harmonogram projektu? Jaká jsou v projektu největší rizika? Ustojí architektura systému reálnou zátěž? ...

Množství neúspěšných softwarových projektů je pro nás varováním, abychom byli obezřetní a neustále hledali způsoby, jak rizikovost vývoje software snižovat. Důsledkem neúspěšného zvládání rizik mohou být obrovské finanční ztráty při produkování nestabilního software. Za dobrý výsledek se nedá považovat ani software rigidně splňující původní požadavky, avšak uživatelsky nepřátelským způsobem. A v mnoha případech dokonce projekty umírají ještě dříve, než se dostane prvotní verze mezi reálné uživatele.

Jsem přesvědčen, že kouzelnou instancí úspěšných projektů je mimo jiné i kvalitní zpětná vazba. Je nutné, abychom byli schopni zpětnou vazbu získávat v co nejkratší době a nezkreslenou a to navíc po celou dobu trvání projektu. Díky zpětné vazbě lépe držíme správný směr a rychleji řídíme projekt směrem k úspěšnému výsledku. Dále popisuji vybrané techniky, které nám pomohou tuto drahocennou komoditu lépe získávat.

Jednotkové testy (Unit Tests)

Zdrojový kód je živý organismus, který prochází neustálým vývojem a změnami. Má-li být projekt úspěšný, je kvalitně navržený zdrojový kód s minimální chybovostí naprosto esenciálním požadavkem. Psaní jednotkových testů je technikou, která nám dává do rukou prostředek pro získávání zpětné vazby o stavu kódu v řádech desítek sekund.

Zpětná vazba je dvojího typu. Dokážeme ověřit, zda-li kód opravdu dělá to, co od něj očekáváme - na co není napsaný test, to jako by v systému ani neexistovalo. Za druhé, dostáváme důležitou informaci o kvalitě návrhu tříd a potažmo celého systému. Na oba indikátory bychom měli být schopni rychle reagovat a problémy řešit.

Programování bez psaní jednotkových testů je mizerná loterie, ve které se obvykle prohrává. Vede k objevování chyb složitějším způsobem a v pozdějších fázích vývoje. Vkrádá se nejistota a strach zasahovat do existujícího kódu, který není pokrytý testy. Téměř se nerefaktoruje, vznikají duplicity a logickým vyústěním je zvyšování nákladů na celý projekt. Bez testů není možné aplikovat některé z dále popsaných technik.

Podobně jako jednotkové testy, potřebuje náš projekt testy integrační. Jedná se o kategorii testů, které prověřují vzájemnou spolupráci více reálných tříd v požadovaných scénářích.

Průběžná integrace (Continuous Integration)

Podívejme se na práci programátora z perspektivy celého vývojového týmu. Jeho chyby totiž mohou nepříznivě ovlivnit zbytek týmu. Nesoustředěný, spěchající nebo unavený kodér promítne změny na server a odchází domů (v horším případě vypíná telefon a odjíždí na třítýdenní zahraniční dovolenou). Nevšimnul si ale, že aplikace nejde zkompilovat nebo že rozbořil pár jednotkových testů. Jiný člen týmu si vezme změny ze serveru a v ten okamžik má problémy i on. Nezkompiluje, prohodí pár jadrných slov (které ve slovníku spisovné češtiny nenajdeme) a začne hledat pachatele. Nakonec se situace nějak vyřeší, ale zpomalení lidí a někdy celého týmu je vždy nepříjemné a drahé.

Řešením je zavedení techniky průběžné integrace. Jedná se o kontrolní mechanismus, který po každém promítnutí změn na server spustí posloupnost ověřovacích kroků. Běžně se provádí kompilace celého projektu, spouští se jednotkové testy, hlídají se pravidla kladená na formátování kódu, provádí se statická analýza kódu, apod. V řádu několika minut dostává programátor, který změny propagoval na server, zpětnou vazbu o tom, že jeho balík změn je nebo není problematický. V zájmu celého týmu by měl na případný problém okamžitě reagovat a uvést verzi na serveru do korektního stavu. Nebrzdit zbytek týmu musí být nejvyšší prioritou.

Týmy, které nemají rozjetý CI mechanismus, se často dostávají do záseků. Reakce na problémy se prodlužují. Dohledávání příčin je složité, protože není vždy snadné určit, který balík změn problém způsobil. Každá propagace změn na server je provázena lechtivým pocitem v podbřišku. Strach a nejistota dlouhodobě paralyzují práci týmu. Není také zřejmé, zda-li je aktuální serverová verze v dobrém stavu a tedy nasaditelná např. do testovacího prostředí.

Revize (Reviews)

Často nedoceňovanou technikou je vzájemné revidování práce. Revidovat můžeme specifikaci požadavků, návrh řešení, zdrojový kód nebo jiné výstupy vaší práce. Odpůrci argumentují vysokou časovou náročností a tedy malou ROI. Pokud to však myslíme s kvalitou aplikace vážně, je tato technika nepostradatelná. Téměř vždy je pohled na řešený problém očima někoho jiného užitečný. Např. při revizi kódu můžeme připomínkovat použitý algoritmus, řešit čitelnost kódu, objevit chyby nebo jiná potenciálně problematická místa. Revizemi si členové týmu vzájemně předávají zkušenosti, učí se a zvyšují bus-factor (zastupitelnost) vývoje částí aplikace.

Kdy a v jakém rozsahu je vhodné provádět revize? Vždy, když je dokončen úkol menšího rozsahu. Ideálně práce, která se vejde do jednoho dne. V méně zkušených týmech mohou být revize delší - objeví se více připomínek. Např. v našem týmu je poměr času strávený prací a její revizí asi 10 : 1. Poměr kolísá v závislosti na složitosti úkolu a aktuální míře “hnidopišství” revizora. Preferujeme spíše méně formální revize, buď za účasti programátora (při složitějších věcech, kde je vhodný vysvětlující komentář) nebo bez něj.

Párové programování (Pair Programming)

Některé věci se lépe dělají ve dvou - třeba programování. Práce v páru je kontinuální diskuze nad problémem a zvoleným řešením. Zpětná vazba je okamžitá. Dochází k vzájemnému revidování a téměř s určitostí bude výsledek společné práce vždy kvalitnější než při individuálním přístupu. Revidování je elementární činností této techniky a platí pro něj všechna pozitiva popsaná výše.

Ne všechny týmy mají vhodné prostředí pro aplikování této techniky. Efektivita je přímo úměrná “sociální kompatibilitě” členů týmu. Někdo je natolik introverdní, že práce ve dvojici jej stresuje a nedokáže ze sebe dostat to nejlepší. Také ne každý úkol je vhodné dělat ve dvojici. Obecně se dá říci, že čím je úkol složitější, tím je párování užitečnější.

Zákazník na pracovišti (On Site Customer)

Zákazník přesně ví co chce, co nejpodrobněji nadefinuje požadavky na systém, udělá se analýza, pak estimace, podepíše se smlouva. Vývojový tým dostane zadání, několik měsíců na něm dělá bez asistence zákazníka a poté, jako mávnutím kouzelného proutku, představí zadavateli funkční a skvělé řešení. … Tak a teď tu o Červené karkulce.

Dobrá tedy, domluvíme raději pravidelné týdenní pracovní porady se zástupcem zadavatele. Lepší, ale stále to není ono. Co takhle mít schopného člověka od zákazníka k dispozici kdykoliv? Mít možnost mu zavolat nebo se s ním sejít ihned, jak si to situace vyžádá? Mnohem lepší. Nebo raději, kdyby fyzicky seděl s týmem v jedné místnosti? Ideální.

Zákazník nám platí a pouze na jeho hodnocení záleží, zda-li bude náš projekt úspěšný. Na začátku vývoje obvykle nemáme dostatečně jasnou představu o tom, jak by měl výsledek vypadat. A i když máme, tak se v průběhu určitě změní. To je zákon softwarového vývoje. Zafixovat požadavky na začátku a dogmaticky trvat na jejich dodržování je možná dobré s ohledem na dodržení podmínek smlouvy, ale málokdy pro výslednou kvalitu. Nebraňme se změnám zadání, buďme na ně připravení. Je naprosto legální, že požadavky se v čase vyvíjí podle toho, jak se tým i zadavatel učí rozumět problémové doméně. Zapojme zákazníka co nejvíce do vývojového procesu a budou z toho profitovat obě strany.

U některých typů projektů, kdy nemáme skutečného zákazníka, je také vhodné zadefinovat interního zákazníka, který bude vystupovat v podobné roli jako skutečný zákazník. Bude zastupovat uživatele vyvíjené aplikace a bude zodpovědný za akceptaci výsledku. Měl by nám být nápomocen při diskuzích a připomínkování a neměl by být z našeho vývojového týmu.

Akceptační testování

Akceptační testování je nezbytný proces pro ověření toho, že aplikace dostála závazkům vyplývajících z požadavků. Akceptační testy tvoří zároveň aktuální dokumentaci k systému. Ideální akceptační testy jsou automatizované, tedy kdykoliv snadno spustitelné. Pokud má aplikace GUI, pak zřejmě bude nutné akceptační testy provádět také ručně. Akceptační testy měl psát zástupce zákazníka ve spolupráci s členy QA týmu. Ne programátor.

Správně napsané akceptační testy dodají kvalitní zpětnou vazbu o použitelnosti a stabilitě aplikace. Procházení jednotlivých testovacích případů je také měřítkem celkového postupu projektu. Získáváme informaci o tom, kolik práce je hotovo a kolik ještě zbývá. Ale znáte to, Paretův princip náš optimismus ve finále stejně zchladí. :)

Malé verze (Small Releases)

Hodně projektů se prodražilo a mnohé úplně zkrachovaly, protože se dostaly do obrovských problémů s vydáním první funkční verze. Na první pohled vypadá snaha uvolnit “superverzi” aplikace jako dobrý nápad. Projektový tým chce vydat aplikaci až v okamžiku, kdy bude splňovat všechny must-have požadavky a ty budou navíc propracovány do nejmenších detailů. Ale my to takhle určitě nedělejme.

Vývoj software je jako většina profesí především o emocích, motivaci, komunikaci, důvěře a dalších netechnických záležitostech. Lidem se mnohem lépe plní rozsáhlejší úkoly, pokud jsou rozděleny na více menších. A navíc každý úkol by měl být zakončen dosažením cíle. Naším prvořadým cílem je vydat aplikaci a dostat ji k uživatelům. Teprve v tomto okamžiku má naše práce nějakou reálnou hodnotu. Do té doby nemáme nic.

Uživatelé ocení, pokud jim brzy dodáme verzi aplikace, která jim přináší nějaký užitek. Navíc rychle získáme zpětnou vazbu z reálného provozu, která je pro nás nejcennější. Vydávejme verze tak často, jak je potřeba. Funkcionalita nemusí být dotažena k dokonalosti, ale nesmíme vypouštět bugy. Pro časté automatizované uvolňování verzí je nutné mít vybudovánu kvalitní infrastrukturu pro Continuous Delivery. Jde o automatizaci vytváření verze, spouštění validačních mechanismů, nasazování do testovací prostředí a ve finále také do produkce. Obsahuje některé procesy známé z Continuous Integration.

Prototypování (Prototyping)

Softwarové projekty jsou plné rizikových míst. Rizikem se může stát například použití nové technologie, návrh architektury, problematický framework, komunikace s novým fyzickým zařízením, napojení na špatně zdokumentovanou službu, apod. Zkušenosti vývojářů se projeví právě ve schopnosti dopředu tato rizika vytipovat a prověřit. Nejrizikovější části projektu řešme co nejdříve. Jako vhodná metoda pro validaci rizik se obvykle jeví prototypování.

Prototyp je zjednodušená a tedy levná varianta aplikace nebo její části, která nám pomůže prověřit potenciální riziko. Při vytváření prototypu není nutné trvat na vysoké interní kvalitě. Důležitá je především rychlost vytvoření a získání zpětné vazby o problému. Po ověření problému život prototypu končí. Produkční kód se začne budovat znovu, tentokrát v produkční kvalitě.

Často se prototypuje uživatelské rozhraní. V takovém případě nejde ani tak o prověřování rizika, zda-li něco nepůjde, ale spíše o ujasnění představy o výsledném produktu. Levně můžeme připravit více variant řešení.

Minimální tržní produkt (Minimum Viable Product)

Jak už bylo popsáno výše, měli bychom se vyhnout snaze dodat veškerou zamýšlenou funkcionalitu najednou. Vývoj by byl zdlouhavý a zpětnou vazbu z reálného provozu bychom získali příliš pozdě. Technika Minimum Viable Product nabádá k tomu, abychom zadefinovali minimální rozsah funkcionality, který reprezentuje vizi vašeho produktu, tu rychle vyvinuli a nasadili do provozu. Získáme tak ohlasy od skutečných uživatelů z reálného provozu. Začneme se rychle učit, jak na produkt uživatelé reagují, jaké by se hodily další funkce a které funkce jsou nevyužívané a tedy nadbytečné. Ideální použití je v situaci, kdy je uživatelská spokojenost skutečně naším prvořadým cílem. To znamená vždycky. Státní IT projekty se nepočítají. ;)

Další členové rodiny technik pro získávání zpětné vazby od skutečných uživatelů jsou Win-loss Analytics, Beta Programs, Focus Groups nebo Market Interviews.

Iterace (Iterations)

Rozdělit řešení rozsáhlého projektu na menší části s jasně definovanými akceptačními kritérii je základem úspěchu softwarového projektu. Zapomeňte na smrtící vodopád a naučme se organizovat práci do iterací. Iterace dostanou náš tým do potřebného pracovního rytmu a získáme důležitou zpětnou vazbu o jeho rychlosti a možnostech. Každá iterace (doporučená délka 1-3 týdny) začíná plánováním úkolů včetně estimace, pokračuje realizací a končí předvedením výsledků a retrospektivou.

První iterace mohou být o sesbírání potřebných informací, vytváření vývojové infrastruktury, zavádění agilních technik, prototypování, vytvoření základních projektových artefaktů, apod. V dalších fázích už řešíme konkrétní požadavky a začínáme produkovat první validní výstupy. Jako první si zvolme nejprioritnější nebo nejrizikovější požadavky, které nás donutí implementovat vertikálně skrze všechny vrstvy aplikace. Učíme se tak co nejrychleji rozumět všem aspektům realizace naší aplikace.

Komunikace v týmu (Team Communication)

Jejda, ty děláš na stejném úkolu jako já!
Vy jste poslední dny dělali tenhle úkol?! Ten má ale aktuálně nízkou prioritu!
Na pár dnů jsem se zasekl s tímhle. -- Proč neřekneš, zrovna tohle jsem nedávno řešil!

Dochází k podobným zmatkům i u vás. Nevíte přesně, kdo na čem aktuálně dělá? Pak je nezbytné zlepšit komunikaci v týmu. Je na zodpovědnosti vedoucího týmu, aby zajistil optimální komunikační prostředí a vzájemnou informovanost všech členů.

Využívejme techniky společných plánovacích schůzek, díky kterým všichni vědí, co se bude dělat a co je prioritní. Zabudujme do svého pracovního dne krátké týmové stand-up rychloporady. Jedná se o efektivní způsob, jak se vzájemně informovat o aktuálně řešených úkolech a problémech. Nezapomínejme na společné retrospektivy, kde se dozvídáme o úspěšnosti plnění našich plánů. Na retrospektivách je také prostor pro diskuzi nad pracovními postupy, které používáme. Nebojme se zahodit postupy, které nefungují a pouze nám přinášejí zbytečnou zátěž.

Postupnými malými změnami v našich pracovních postupech můžeme ve výsledku dosáhnout velkého zlepšení. A o to nám přeci jde. Vím to, protože jste tento článek dočetli až sem. ;)

4 komentáře:

  1. Ještě bych jako druh zpětné vazby doplnil monitoring a skenování logů na výjimky. Je plus, když už k chybě dojde, na ni zareagovat (optimálně i opravit - např. při chybě v datech) dřív, než zákazník stihne vyplnit ticket.
    Každopádně díky za souhrn, rozhodně se z toho máme ve značném množství bodů co inspirovat.

    OdpovědětVymazat
  2. Výborný článek. Mimochodem moc by mě zajímalo v kolika firmách se takto skutečně vyvíjí. Máte nějaký odhad?

    OdpovědětVymazat
    Odpovědi
    1. To by mě taky zajímalo. ;)
      Obávám se, že spousta firem získávání zpětné vazby v průběhu projektu krutě podceňuje. A pak se diví, kolik pracovního času zbytečně propálí. Nám ve firmě to také občas neklapne. :( Je však dobré si výše popsané souvislosti uvědomit a dokázat v týmu obhájit jejich prosazení a návratnost.

      Vymazat