Zobrazují se příspěvky se štítkemagile. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemagile. Zobrazit všechny příspěvky

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. ;)

pondělí 1. října 2012

Společné vlastnictví kódu

... nejvyšší forma týmové spolupráce

Franta je na dovolené, proto si musí zákazník počkat tři týdny na opravu komponenty.
Vím, že je kód této třídy ve špatné kvalitě, ale není to můj problém. Obrať se na autora!
Už teď se mi při pomyšlení na refaktoring Robertova kódu dělá špatně! Tohle po mně vážně nechtěj!
Jsem specialista na UI a nebudu zasahovat Martinovi do kódování middleware.
Ludva se otrávil methylalkoholem a oslepl. Bude trvat týdny, než to někdo převezme.

Pokud se váš vývojový tým dostává do podobných situací, zřejmě by vám pomohlo praktikování společného vlastnictví kódu (Collective Code Ownership). Jedná se o jednu z technik metodiky extrémního programování (XP). Jednoduše by se dalo CCO definovat takto:

"Všichni sdílí zodpovědnost za kvalitu kódu. Každý může udělat změnu v jakékoli části kódu. Od všech se očekává, že odstraní problémy, na které v kódu narazí."

Pojďme se podívat, jakou přidanou hodnotu nám společné vlastnictví přináší.

Vyšší kvalita kódu

V socialismu se kolektivní vlastnictví příliš neosvědčilo. Ve výsledku všechno patřilo všem a nikomu a stav věcí podle toho vypadal. Vývojový tým je však specifická struktura, která při správném vedení a správných charakterových vlastnostech jejich členů, dosahuje spoluprácí vyšší výkonnosti. Skupinová zodpovědnost vede k tomu, že vývojáři usilují společně o vysokou kvalitu kódu. Špatný kód napsaný kolegou je i můj špatný kód. Kolega by měl pod závazkem společného kódu cítit větší zodpovědnost a snažit se o potřebnou kvalitu. Já bych naopak měl na nekvalitní kód upozornit a nebo jej přímo zlepšit. Třeba refaktoringem nebo vylepšením návrhu.

Pokud je váš kód zveřejňován a běžně posuzován jinými lidmi, zvyšuje se vaše motivace ke kvalitnější práci. Profesionální vývoj software je o osvojení správných návyků. Nad návyky nepřemýšlíte, děláte je automaticky. Pokud jste junior vývojář, bude pro vás práce v týmu s CCO obrovskou školou. Na druhou stranu někteří senior vývojáři mohou mít problém s tím, že někdo jiný jim vidí do kódu a navíc si ho dovoluje měnit! Považují svůj kód za natolik intimní záležitost, že si ji brání jako poklad. Někdy je to také z důvodu, že jejich kód je nečitelný, tedy špatný a sami to vědí.

Poznámka: Mezi atributy externí kvality kódu patří bezchybnost, použitelnost, efektivita, spolehlivost, integrita, přizpůsobivost, přesnost a robustnost. Mezi atributy interní kvality kódu patří udržovatelnost, flexibilita, přenositelnost, znovupoužitelnost, čitelnost, testovatelnost, srozumitelnost.

Vzájemná zastupitelnost

Tradičním problémem vývojových týmů je koncentrace znalostí pouze u konkrétních lidí. Autor komponenty je jediný, kdo zná její kód a pro ostatní se jedná o černou skříňku. Někomu pocit nepostradatelnosti může dělat dobře, ale pro flexibilitu vývoje je to špatně. Dovolená, nemoc nebo jiná absence zablokuje práce na komponentě na dlouhou dobu. Úplná ztráta člena týmu pak může způsobit katastrofu.

Společné vlastnictví kódu tyto problémy řeší poměrně efektivně. Dobrá znalost codebase je dovedností každého člena týmu. Oprava chyby, doplnění funkcionality nebo jiný zásah do kódu někým jiným než autorem? Není problém. Ty máš čas, tak to prosím udělej.

Co vlastně potřebujeme?

Společné vlastnictví kódu nelze efektivně aplikovat bez dalších podpůrných technik XP. Jinak se výsledek může významně lišit od našeho očekávání.

Vývojáři si musejí rozumět. Jejich kód musí být vzájemně snadno pochopitelný. Je nutné zavést jednotný kódovací styl (Coding style). Měly by se ctít zásady čistého kódu (Clean Code) a jednoduchého návrhu (Simple Design). Vysoká čitelnost kódu je zásadní požadavek na práci profesionálního vývojáře. V případě CCO je jeho důležitost ještě umocněna.

Kvalita existujícího kódu je přírůstkově vylepšována postupným refaktoringem (Refactoring). Refaktoring úzce souvisí s pokrytím kódu pomocí testů. XP aplikuje metodiku testy řízeného vývoje (Test-Driven Development). Bez správného pokrytí kódu jednotkovými testy se nedá CCO použít. Vycházíme-li z definice CCO, kdy každý má právo a povinnost kód vylepšit (refaktorovat) a rozšířit, je regresní funkce jednotkových testů nenahraditelná. Pokud by testy nad kódem nebyly, každý by se bál do stávajícího kódu zasáhnout.

Další úrovní zajištění je technika postupné integrace (Continuous Integration). Po každém promítnutí změny v kódu do společného prostoru je proveden kontrolní serverový integritní build, jehož součástí by mělo být i spuštění jednotkových testů. V případě zanesení problémů je autor změny ihned vyzván k opravě. Touto technikou je zajištěna integrační validita kódu na týmovém serveru.

Tým by měl používat verzovací systém (Version Control) pro správu zdrojového kódu s podporou vícenásobného checkoutu. Při CCO dochází častěji k situaci, kdy více uživatelů v jednom období edituje lokálně stejný objekt. Systém by měl mít kvalitní nástroj pro následné slučování změn. Doporučuje se u existujícího kódu dělat úpravy menšího rozsahu a tím pádem častěji vracet změny na server.

Efektivní podpůrnou technikou je párové programování (Pair Programming). Programování ve dvojicích zlepšuje komunikaci, urychluje šíření znalostí v týmu, díky online revizím návrhu a kódu zvyšuje výslednou kvalitu. Většina kombinací [nováček|pokročilý|expert]{2} je ve výsledku efektivní, kromě nováček + nováček - mentor. Páry by se měly měnit a je nutné vytvořit unifikované programové prostředí u všech párovačů. Není nutno párovat po celou dobu práce.

Rizika

Nedostatečná znalost problematiky jednoho z členů týmu může způsobit problematický zásah do existujícího kódu. Jako pojistky máme jednotkové testy, které by zároveň měly dokumentovat návrh příslušné části systému. V případě, že znalosti člena týmu jsou špatné, mohlo by zabrat nasazení párového programování.

Nikdo se nehlásí k zodpovědnosti za daný kód (No Code Ownership). Takového rizika se mohou obávat především projektoví manažeři. Všem zúčastněným musí být zřejmé, že zodpovědnost za každou část leží na celém týmu. Postoje typu "Já mám svůj kód v pořádku a tento mě nezajímá" je potřeba eliminovat.

Nedodržení souvisejících agilních technik oslabí výhody CCO a možná jej úplně zabije. Je na zodpovědnosti vedoucího týmu, aby zajistil vytvoření a provoz správně nastaveného agilního prostředí.

Alternativy

Pokud se codebase striktně rozdělí podle členů týmu a vzájemně si do kódu nezasahují, mluvíme o silném vlastnictví (Strong Code Ownership). Méně striktní je slabé vlastnictví (Weak Code Ownership). Kód je stále rozdělen podle správců jednotlivých komponent, ale jiný člen týmu může po svolení od správce kód změnit.

Závěrem

Pro malé a agilně řízené týmy se jeví CCO jako přirozené řešení požadavků na kvalitu a zastupitelnost. U korporátních neagilně řízených týmů by se na propagátora CCO mohli dívat jako na Marťana, můžete to zkusit. :) Pokud pro vás není agilita sprosté slovo, určitě stojí za to se nad dopady CCO zamyslet.

Rád bych článek doplnil o nějaké praktické postřehy, ale ve firmě se nám zatím nepodařilo vytvořit dostatečné prostředí pro provozování společného vlastnictví kódu. Výhody jsou zřejmé a rádi bychom stávající slabé vlastnictví přetransformovali na společné. Pokud máte vlastní zkušenost, prosím o připojení komentáře. Děkuji.

Zdroje

The Art of Agile Development: Collective Code Ownership - můžete si přečíst odpovědi na otázky, které zřejmě vznese každý, kdo o přechodu na CCO uvažuje.