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

pátek 30. listopadu 2018

Refactoring, část IV: Odstraňování duplicit

Co je špatně?

Duplicitní kód se sice rychle a snadno generuje (podobně jako diplomky politiků), ale později se ve zlém vrátí tím spíš, čím víc "klonů" existuje. Scénář katastrofy je obvykle rutinní:
  1. Uživatel nahlásí chybu nebo je třeba implementovat novou vlastnost
  2. Chyba se opraví - čili klon se liší od svých pravzorů
  3. Jiný uživatel reklamuje chybu v jiném klonu
  4. Chyba se opraví - obvykle jinak než v prvním klonu.
  5. Ostatní klony jsou neopravené. Trpí stejným problémem? Netrpí jím? Jak to, že fungují správně? Je část klonovaného kódu v jejich případě zbytečná? Nebo nefungují správně, jen ještě nikdo v jejich případě chybu nereklamoval?
  6. Rozdíly narůstají, chyby někde mizí, jinde zůstávají nebo se dokonce mění. Technický dluh roste exponenciálně.

Konfigurace CPD, Copy And Paste Detector


CPD je součástí PMD maven pluginu, čili když už, nejspíš budete používat oba. Kontrolám PMD samotného se ale vyhnu, to si najděte jinde ;)
CPD porovnává soubory a hledá v nich opakující se sekvence "tokenů". Algoritmus lze trochu konfigurovat, viz dokumentace pluginu.

                <plugin>
                    <artifactId>maven-pmd-plugin</artifactId>
                    <version>3.11.0</version>
                    <executions>
                        <execution>
                            <id>cpd</id>
                            <goals>
                                <goal>cpd</goal>
                            </goals>
                            <phase>verify</phase>
                            <configuration>
                                <skip>${cpd.skip}</skip>
                                <minimumTokens>100</minimumTokens>
                                <excludes>
                                    <exclude>**/en/**/*</exclude>
                                    <exclude>**/generated/**/*</exclude>
                                </excludes>
                            </configuration>
                        </execution>
                        <execution>
                            <id>cpd-check</id>
                            <goals>
                                <goal>cpd-check</goal>
                            </goals>
                            <phase>verify</phase>
                            <configuration>
                                <skip>${cpd.skip}</skip>
                                <printFailingErrors>true</printFailingErrors>
                                <verbose>true</verbose>
                            </configuration>
                        </execution>
                    </executions>

Pokud plugin najde dvě stejné sekvence delší jak 100 tokenů, shodí build a vypíše nalezené duplicity. Ty pak budete muset řešit.

Řešení

Fuj: Přehodím řádky

Ano, z pohledu CPD jste z toho venku, ale po pravdě ... vážně myslíte, že jste se problému zbavili? Ve skutečnosti jste se zbavili jen jeho hlášení, problém trvá. Navíc jste si ještě zhoršili orientaci ve třídě.

Fuj: Zvýším minimumTokens

... a zbavím se hlášení. Problém trvá, ale co hůř, ono to často ani nezabere, protože těch duplicit je víc, byť i jen na málo řádcích. Pointa je právě v tom, co vlastně je ten "token". Ta stovka je docela rozumná, proto je taky jako default.

Exkluze

Ta funguje stoprocentně. Není ovšem určená k ignorování skutečných problémů, nýbrž k ignorování falešných hlášení problémů. Může být nalezená duplicita, skutečná duplicita, falešným hlášením? Je třeba si uvědomit, že cílem není zbavit se jakýchkoliv duplicit, ale zbavit se hrozících problémů s duplicitami, zlepšit architekturu, uklidit.

Do exkluze tudíž typicky dávám jednak kód generovaný, se kterým nic nenadělám, jednak třeba JPA entity, které mohou mít velmi podobné vlastnosti, přestože vzájemně nijak nesouvisí. To, že se něco stejně jmenuje, nemusí znamenat, že je to duplicitní - a CPD s oblibou hlásí stejné čtyři settery a gettery, jdoucí za sebou.

Problém může být, pokud nemáte žádnou konvenci, jak třeba JPA entity odlišit od jiných tříd. Hodně štěstí ;-)

Vyvedení do samostatné metody ve stejné třídě

Samozřejmost, pokud je duplicitní kód jediné třídy. Nebo ne? Pokud na 10 řádkách máme 8 lokálních proměnných, tohle nebude smysluplná cesta. Můžeme je zkombinovat do jednoho výpočtu, jenže ... to zásadně zhorší čitelnost, takže taky ne. Takže jiná možnost.

Vyvedení do společného rodiče

Je to možnost, ale je třeba zvážit, zda vyváděná funkcionalita do rodiče opravdu patří. Pokud ostatním potomkům rodiče je tato funkcionalita cizí a nemají s ní nic společného, nedělejte to. Jedině že byste stvořili dalšího "mezirodiče", ale pokud mu nedokážete dát smysluplný název, budu se opakovat - nedělejte to; příliš vysoká hierarchie rodičů a potomků je další problém, čili je tu riziko, že odstraněním jednoho problému vytvoříte jiný.

Vyvedení do "utility" nebo "cizí" třídy

Utilitky, helpery, atp., jsou víceméně neobjektová věc, na druhou stranu netrpí problémy s dědičností, dobře se na ně píšou testy, takže tuhle možnost vůbec nezatracujte.
Podobně je možné, že víte o třídě, kam tento kód vlastně i logicky patří a lze ho odsud sdílet na potřebná místa. Předpokladem je, že vyvedenému kódu dokážete vytvořit nějaké pěkné smysluplné API. Ale to se vlastně týká každého "vyvádění".

Vyvedení do default metody interface

Óóó, jak snadné a efektivní!
Jenže to má háček - nese to riziko. Implementace z abstraktního rodiče, implementující stejnou metodu, má vyšší prioritu než jakýkoliv interface!!!
Vážně si rozmyslete, jestli vyváděný kód může opravdu sloužit jen jako pouhý "default". V případě kolizí pak musíte metodu stejně přetížit a z těla volat Iface.super.metoda(), jinak řečeno musíte říct, kterou z dostupných implementací chcete používat.
Pokud máte ale v rodiči metodu implementovanou, complier vám nic neřekne a neporadí, předpokládá, že to vážně chcete.

Silver bullet: přečtěte si to

Překvápko: někdy není třeba duplicitní blok "vyvádět", stačí oba duplicitní bloky naprosto stejně zjednodušit. Třeba odstranit 10 opakovaných přetypování. Nebo jen něco, co se na obou místech otravně opakuje, vyvést ven a sdílet. Výsledek? Ubyde tokenů, jak prosté, už to nevypadá jako hloupý copy and paste, jen něco, co dělá stejnou věc, a to už není známka copy and paste.

Svět je zase v pořádku :-)

neděle 22. února 2015

Refaktoring, část III.: HashMapové peklo

Proč?

Protože HashMap je objekt, do kterého lze vložit leccos. A to doslova leccos, a vždycky pak musíte předpokládat, že tam různá leccos jsou. Vy ale máte objektový jazyk (nebudeme slovíčkařit), a tak pro objekty obvykle nacházíte jména, definujete jejich chování a vlastnosti. A to je to místo, kde to skřípe - mapa je něco, co objekty dostává s nějakým klíčem a pod tím klíčem je zase vrací. A je v tom fakt dobrá - jenže vám neumožní nic víc.
Je je to jako kdybyste v kuchyni měli obrovský pytel a do nej házeli nákup - pytel brambor, konzervy, kořenky, láhev piva a láhev mléka ... a když něco chcete, otevřete pytel a hledáte. Možná těch pytlů máte více a všechny vypadají stejně.
Mapy jsou zkrátka vhodné na vkládání neomezeného počtu věcí ideálně stejného typu pod klíčem stejného typu a pak k jejich vyhledávání podle tohoto klíče.

Naopak nejsou vhodné na vkládání omezeného množství dat, které navíc dokážeme snadno pojmenovat, odlišovat, známe chování, možnosti, můžeme (a chceme!) ho dokonce zdokumentovat, ať už méně nebo více.

Jak takové peklo vzniká?

Jak jinak, vzniká z lenosti programátora - kterému přijde hloupé a pracné zakládat spousty nových tříd, které mají jen atributy a k nim příslušející gettery a settery. Ten pak udělá v nejhorším případě toto:
Map data = new HashMap();

Samozřejmě pak nevíte, co v datech máte, a musíte pořád dokolečka hledat klíče a všechny možné varianty typů hodnot, které se mohou vyskytnout.

Nicméně i když onen programátor mapu otypuje, čili vytvoří
Map<String, String> data = new HashMap<>();
nemáte vůbec vyhráno. Pořád totiž nevíte, pod jakým klíčem je třeba příjmení osoby ... a jestli tam vůbec je. Ani konvence vás nezachrání, pořád to může být surname, lastname, lastName ...
Napsat pak takový kód lze samozřejmě celkem rychle, pokud nevyvíjíte něco složitého, ten den to udržíte v hlavě. Dokonce na to lze napsat testy, nicméně nic z toho vás nevysvobodí, až se v kódu po půl roce provozu objeví chyba a vy nebo někdo jiný jí bude muset opravit. Nebo ještě druhá varianta, dostane za úkol ho rozšířit o další funkcionalitu ... najednou se projekt prodraží a nikdo nedokáže říct ani řádově přesný odhad, kdy to bude hotové.

Pokud pak používáte JSP, výsledek programátor nejspíš strčil do pageContext, což je vlastně taky hash mapa, resp. celá hierarchie map, které kontejner průběžně zahazuje nebo maže dle kontextu.

Pokud takový kód psal externista, je nejspíš už bezpečně daleko ... a je to vaše starost.

Jak z toho ven?

Tohle určitě nechcete slyšet, ale máte smůlu - čeká vás dřina!
Pokud používáte JSP, jakkoliv tuto technologii spousta lidí nemá ráda, vám tentokrát pomůže. Ona se totiž dívá na typ objektu, se kterým zrovna pracuje - pokud je to Map, zavolá get("xxx"), jinak zavolá getXxx(), takže jí můžete pod rukama prohodit instance v kontextu, a ona se přizpůsobí.
I tak to ale bude dřina, nicméně tady je její druhá hranice - pokud tedy programátor nevymýšlel jiné šílenosti, ty by jinak bylo vhodnější vyřešit předem ...

Pak už nezbývá než otrocky si na papír vypsat všechny klíče, které se v mapě vyskytují, mapu nahradit vhodně pojmenovaným objektem, a připravit pro klíče stejnojmenné settery a gettery; pozor, přejmenovávání doporučuji nechat na později!
Pokud si nejste jisti typem atributu, pro začátek doporučuji třeba String - kompilátor vám už v editoru vyznačí, kde to nesedí. Naopak Object nedoporučuji, protože se o případné chybě nedozvíte.

No a to je vše. Je? Není. Ještě musíte vychytat některé důsledky. Co když někdo volal isEmpty(), size(), používal výstup z put()?

Mně mapy ale nevadí

Někteří lidé pořád nechápou, co je na tomhle použití map špatně. Tak ještě ... nemůžete plně využít dědičnost, overloading, generiky, přidávat objektům metody, atd. Zkrátka to, co vám jazyk umožňuje, využíváte jen z malé části, neefektivně. Dokonce to hloupoučké maličké leč poctivé POJO zabírá i méně místa v paměti ...
Ale hlavně - čitelnost kódu, to, že vždycky víte, s čím pracujete, kolik to má metod a jakých, co to dělá, k čemu to je, to je to, kde mapa vždycky prohraje, říkajíc
"No, vy mi něco dáte, řeknete mi, jak to poznám, a až to budete chtít, tak já vám to zase vrátím. Jo a taky vím, kolik už toho držím. Co? To není moje starost ... že mám reprezentovat osobu a vracím vám SPZ? Tomu já nerozumím, co mi je do toho?! Když mi dáte konexi do databáze, budu jí držet taky, no a?! Umím to, všechno udržím!"

Řekl bych, že v ten moment tomu nerozumí i někdo jiný ... a ten bude autora kódu proklínat - že na to nevěříte? Jóó, kdo ví ... ;)

sobota 17. května 2014

Refaktoring, část II.: Technický dluh

Aneb kapitola (nejen) pro manažery, ekonomy, zkrátka byrokracii, která rozhoduje o investicích, financování, směrování projektů - a taky o tom, kdy se projekt uzavře jako "hotový".
Původně jsem chtěl jít rovnou na zdrojáky, ale událo se něco, co mě přimělo vložit ještě jednu kapitolu. Zjistil jsem totiž, že u nás se o technickém dluhu až zase tak moc nemluví. Nicméně programátorší "guru" o této metafoře mluví už docela dlouho:

Co je to?

Manažeři i zákazníci milují vodopádový model: objednávka, zadání, analýza, zhotovení, akceptace, zaplacení. Nic složitého to přece není, vypadá to triviálně a jednoznačně. Ti zkušenější už ví, že každá ta fáze skýtá mnohá nebezpečí a pasti. Obecně nejednoznačnost a nedotažení každé té fáze - příčinou je obvykle neznalost přesných požadavků a neznalost způsobu výroby na druhé straně.

Zjednodušeně řečeno, technický dluh vzniká vždy, když kdokoliv na projektu odloží něco, o čem dobře ví, že je třeba udělat, ale odloží to - ať už se to týká analýzy, testů, dokumentace, vyčištění kódu.

Jak moc to vadí?

To je různé - asi jako inflace, státní dluh, vaše dluhy; proto se tomu říká technický dluh. Jsou to nedodělky, ale ne jen ty, které vidíte při předvádění aplikace. Ty skryté jsou daleko nebezpečnější. Proč? Nedodělky, které vidí uživatel, viděli všichni během vývoje projektu, a došlo k nějakému konsenzu, že jde o kompromis, se kterým uživatel dokáže žít.
Nedodělky, které ale vidí jen programátoři (pokud je vůbec někdo vidí), mají vlastnost právě té inflace - je to exponenciální funkce. S každou další iterací se umocňuje vliv dluhu, veškerá jeho negativa. Dlouho to nemusí vadit, ale když nad technickým dluhem ztratí vývojový tým kontrolu, už není cesty zpět a vývoj projektu skončí s potupnou ztrátou a obviňováním všech, kteří se na něm podíleli, navzájem.


Exponenciální funkce

Je jasné, že udržet projekt bez dluhů je prakticky nemožné. Vždycky se dá všechno udělat lépe. Na druhou stranu, když dluhy nesplácíte, špatně skončíte. Z vlastní praxe bych to rozdělil na takové tři kategorie (v horším případě fáze) ...

Dluh pod kontrolou

Dobrý stav, který znamená, že projekt má budoucnost a přestože obsahuje pár chyb (někdy i hodně), má smysl v něm pokračovat. Příznaky jsou následující:
  • vývojáři dávají celkem rozumné odhady pracnosti
  • nikdo není nervózní, panují dobré vztahy
  • vývojáři se těší na další úkol
  • většinou se stíhají termíny

Zadlužení

To už je horší stav, ale není nezvladatelný. Nesmí se podcenit - i za cenu oddálení termínu předání další verze je nutné dluh udržet nebo ideálně snížit. S každým dalším nárůstem se situace zhoršuje. Příznaky tohoto stavu jsou takové:
  • vývojáři pracují přesčas, často neplaceně a dobrovolně
  • zpravidla se nestíhají termíny, předání verze se oddaluje i opakovaně
  • často se mění analýza během vývoje
  • množí se požadavky na "až"
  • horší se přesnost odhadů pracnosti - obvykle se podstřelují v toužebné snaze všech stihnout termín
  • tendence přidávat lidi do zpožděného projektu
Management nechápe, proč se dříve termíny stíhaly a teď ne, má tendenci přitlačit, motivovat, ale prakticky dosahuje jen jediného - zvýšení tlaku a stresu, což často končí odchodem zaměstnanců, zpravidla těch nejlepších v první řadě, těch nejhorších potom v řadě druhé. Zůstávají jen bojovníci - pokud se dokážou vzepřít veškeré nepřízni, má projekt ještě naději.

Exekuce se blíží

V tuto chvíli se podívejte opět na ten graf exponenciální funkce. V určitém bodě se dostanete přes hranici, kdy vývoj dalších verzí projektu stojí ohromné zdroje a úsilí a jste ve stavu, kdy je extrémně těžké s tím něco začít dělat.
  • vývojáři často mění své odhady, klidně o dva řády - z hodiny je týden, z týdne 20 minut.
  • jakýkoli termín vyvolává šílený smích vývojářů
  • panuje nervozita a dochází k hádkám a práskání dveřmi
  • manažeři zakazují jakoukoliv údržbu, dovolené, a snaží se do projektu dostat nové lidi - a to jakékoliv
  • neprovádí se analýza, nebo jen povrchně
  • nehledí se na žádná kvalitativní měřítka
Je téměř vyloučeno, abyste se dostali z této fáze zpět. Pokud chcete v budoucích projektech uspět, uvědomte si, co jste zanedbali dříve, podcenili. Není to o tom, že jste měli požadovat vyšší cenu nebo sehnat více lidí.  Vždy potřebujete čas a vždy potřebujete nějakou stabilní kvalitu. Na tom, co děláte dnes, budete stavět zítra.

Udržování dluhu pod kontrolou

Všechno je vlastně docela snadné a pro řadu souvisejících problémů dokonce existují nástroje.

Odhadování pracnosti

To je problematika, na které často stojí váš úspěch - odhadnout, kolik času budete potřebovat na zhotovení něčeho, o čem ještě nemáte "ani páru", je trochu neřešitelný úkol. Existuje na něj řada strategií a doporučení, ale vždy ke kvalifikovanému odhadu potřebujete přehled. Odhad navíc nemůže být definitivní - je to jen odhad, že ...

P: "Devět žen neporodí ani jedno dítě za jeden měsíc. Chápeš?"
M: "Jojo, tohle ví každej, to znám ... Ale Ty jsi chlap!"
P: "Máš pocit, že devět chlapů nějaké dítě porodí?" 

Jak se projevuje technický dluh na odhadu? Představte si, že máte nějakou knihovnu, kterou lehce zanedbáváte - používáte jí ale v aplikaci bez problémů. Přijde ale nový úkol pro aplikaci, při kterém ale zjistíte, že v knihovně je chyba. Také zjistíte, že chybu jste v jiných aplikacích, které na ní už narazily, obešli. Jenže tuto obezličku v nové aplikaci uplatnit nemůžete, protože je v rozporu se zadáním - a navíc jste tehdy nepsali ani testy, takže ani nevíte, co všechno se opravou naopak rozbije.
A tak vám nezbude, než chybu opravit, čímž ale možná rozbijete již hotové aplikace s obezličkou. Tudíž pak budete muset i dopsat testy a opravit i tyto aplikace.

A teď se krátce zamyslete - jaký asi byl původní odhad? Kolikrát ho během opravy změníte? A jaká byla výsledná pracnost? Tím to ale nekončí - opravené aplikace bude možná třeba také distribuovat, takže nám vzniká další pracnost.
Ufff, tohle bolelo. A ještě bude, protože všem musíte vysvětlovat, co se vlastně stalo a proč - a čas běží dál a náklady rostou.

Psaní automatických testů

Automatický test je vlastně další kód, který programátor napíše nejlépe předtím, než začne programovat nějakou funkcionalitu aplikace. Test není součástí aplikace, ale verzuje se spolu se zdrojáky, a moderní programovací jazyky velmi pečlivě zohledňují testovatelnost.
Není žádná výmluva pro nepsaní testů, nikdy. Už dobrých 20 let se považuje za prokázané, že automatické testy vedou k
  • rychlému nalezení chyb nového kódu
  • ujasnění designu a zpětné vazbě analýze dříve, než je aplikace hotová
  • rychlému nalezení chyb, které způsobily opravy na jiném místě
  • konzistentnímu refactoringu (nic se nerozbije)
  • dokumentaci funkcionality (test minimalisticky ukazuje, jak se funkcionalita používá)
Naopak prosby nebo dokonce zákazy manažerů, aby se psaní testů odložilo, protože není čas, končí tak, že
  • dostanou funkcionalitu ještě později
  • druhý den se opravuje oprava dne předchozího, den za dnem
  • nikdo neví, co to vlastně dělá a k čemu to je (brzy ani autor)
  • jakákoliv změna v kódu znamená nutnost manuálního přetestování skoro celé aplikace, protože nikdo neví, co všechno změna ovlivnila

Refactoring

Refactoring se přímo zaměřuje na snižování technického dluhu. Obvykle je dobré začít psaním testů, dopisováním //FIXME a //TODO, případně komentářů, kam si zapíšete své objevy proč a co se v tom daném místě děje, co je na tom špatně, jak by to mělo být správně. Tyto komentáře neslouží k tomu, aby v kódu zůstaly, ale abyste se při své analýze neztratili.
Musíte postupovat opatrně, protože se pohybujete na "minovém poli" (proto kód chcete přece refaktorovat), a krok vedle může znamenat, že své úpravy zahodíte (dokud je ještě čas).

Refactoring předně slouží k tomu, aby byl kód čitelný, měl jasné odpovědnosti a funkcionalitu, choval se předvídatelně a funkcionalita byla vždy k nalezení tam, kde jí člověk hledá. Potom se na kódu teprve dá stavět něco dalšího, kde nebudete muset vymýšlet žádné obezličky.

Refactoring nikdy nekončí - ke každému kódu se po čase musíte iterativně vracet, protože jak se rozvíjí aplikace, je občas třeba změnit trochu i uspořádání kódu, sloučit věci, které se původně zdály rozdílné, ale nejsou, rozdělit věci, které původně dělaly téměř totéž, ale už dávno to není pravda, atd.
Ač se to některým lidem zdá pořád neuvěřitelné, nečitelnost kódu, velké množství duplicit a slabé pokrytí testy mají extrémní vliv na jakýkoliv budoucí rozvoj, daleko větší než sebekomplikovanější zadání.

... a odkládání

Pokud se údržba zanedbává, problémy na sebe nenechají dlouho čekat:
  • náklady na rozvoj aplikace jsou čím dál vyšší, neúměrně požadavkům zákazníka
  • opravené chyby uživatel opět hlásí jako neopravené (našel je i jinde)
  • aplikace se chová nekonzistentně (a uživatel jí nenávidí)
  • aplikace potřebuje více paměti a je pomalá
  • vývojáři trvá velmi dlouho, než zjistí, co má vlastně dělat, těžko se orientuje
  • n testů téže věci a podobná věc není otestovaná vůbec
  • nepřehledná dokumentace, nepřehledné testy, nepřehledná aplikace
  • vývojáři nenávidí aplikaci a po čase odchází jinam (nepodceňovat!)
Často se ale zapomíná také na to, že jsme jen lidé a zapomínáme. O týden odložená údržba už znamená, že se v ošklivém kódu přestává orientovat i jeho autor, a nejen rozvoj aplikace, ale i její údržba stojí více, je namáhavější a také při ní pravděpodobně vznikne více chyb.Je to podobné jako s úvěry - u některých můžete odložit několik splátek, ale pak je bude mnohem těžší dohnat. Možná to už nezvládnete ...

Nástroje

Co se týče sledování odhadu technického dluhu u nás používáme SonarQube; V této aplikaci je i řada dalších metrik kvality software a dá se říct, že je to jediná aplikace, kterou znám, která umí zobrazovat i historii různých hodnocení projektů a dá se i zhruba použít k porovnávání kvality. Podotýkám, zhruba, protože žádný software nemůže posoudit to, jak vaše aplikace plní požadavky uživatele a zákazníka.

No a pokud jde o nástroje pro vývojáře a tvorbu automatických testů, refactoring a vývoj obecně, ti už "ty svoje" nástroje určitě dobře znají ;-)

neděle 13. dubna 2014

Refaktoring, část I.: Proč já?!

Úvod

Četl jsem už spoustu článků i blogů o tom, co programátor má nebo nemá dělat, a jak užitečný a dokonce nutný je refaktoring. Nikde se ale moc nepíše o strategiích refaktoringu a dost lidí se ho pořád i tak trochu bojí. Tož jsem se rozhodl, že o tom něco napíšu sám, neb s tím mám dlouholeté zkušenosti.

V roce 2007 jsem přišel do firmy ICZ, jako "junior", který o Javě "už někde slyšel". Ne jako nějaký odborník, zkraje jsem byl zkrátka tolerován. Ne na dlouho ... po dvou měsících na jednom projektu ze mě udělali vedoucího projektu jiného, o dost většího, který dodnes (2014) přežil leccos, neustále narůstá a komplikuje se, ale paradoxně se i zlepšuje jeho stabilita i udržovatelnost - přitom na něm dělá pořád míň a míň lidí. Jak to?
Postupně začnu popisovat své zkušenosti - řekněme takovou formou "občasníku". Nebudu vám vtloukat do hlavy agilní metodiky, scrum, extrémní metodiky, zkrátka, nebudu vám říkat, co máte dělat. On totiž každý projekt potřebuje "svoje". To samozřejmě není nic proti těm metodikám, je dobré je znát, a vybírat si vhodné "zbraně k boji".
Jen pro zajímavost: "můj" projekt má po 11 letech vývoje zhruba 500 000 řádek kódu (kódu v Javě! čili ne javadoc, ne prázdné řádky a dokonce ani webové stránky - JSP).

Jsem nový v týmu

Tak to začalo. Návyky z minulé práce se tu nevedly. Nové návyky byly pro mě zvláštní, nové, chvílemi jsme se vzájemně odsuzovali, ale ve výsledku jsme stále táhli za jeden provaz.

Fáze 1: První zákaz

Když jsem uviděl víc jak 1000 řádkovou třídu, servlet, dokonce s jedinou metodou, chvíli jsem na to zoufale zíral, a pak začal refaktorovat. Když jsem se zmínil šéfovi, bylo mi řečeno, že nemám "spravovat co není rozbité" a "zákazník tohle neplatí".
Obvyklá rada zní - z takové firmy rychle utíkejte!
Mám takový "zlozvyk", vlastně dva:
1) Neutíkám z boje, nerad se vzdávám, radši zkusím, co se s tím dá udělat.
2) Nenechám si zakázat něco, o čem vím, že to dokážu dotáhnout do konce.

Fáze 2: Tolerance

Ukázalo se, že porušení zákazu nemá negativní důsledky. Pořád mi to nikdo nevěřil, někteří programátoři mě považovali za magora, kamikaze, a někteří to vzali jako výzvu. Výsledkem bylo mlčení na obou stranách, termíny se stíhaly, všechno víceméně fungovalo jako předtím, ok.
V tuhle chvíli se musím zastat možná i vašich projekťáků: postavte se do jejich role, pochopte, o co jim jde:
1) Více změn, větší pravděpodobnost chyb.
2) Změny v předaném hotovém kódu mohou způsobit změnu chování a ta se těžko vysvětluje zákazníkovi - ten NECHTĚL, abyste cokoliv z toho měnili.
3) Pokud se nestihne termín, vaší firmě hrozí penále a pokud navíc měníte něco, co nikdo nechtěl měnit, bude to bez slitování. Zákazníka refaktoring nezajímá, berte to na vědomí.

Musíte získat důvěru šéfa, ne hysterčit. Musíte být dost pečliví a dobří na to, aby se za vás mohl s čistým svědomím postavit.

Situace, kdy v týmu panuje nevraživost, když se se svými šéfy nechcete bavit o tom, co děláte, a vzájemně si lžete, je opravdu chvíle vhodná ke zvážení odchodu jinam. Ale pokud se to ani nepokusíte změnit, pravděpodobně jinde dopadnete stejně.
Většina programátorů jsou vysokoškoláci, odborníci, experti, dokonce se dá říct vědci. Pokud něco víte, musíte umět taky najít argumenty, důkazy, a mít trpělivost je ukázat a vysvětlit každému, komu bude třeba. Já vím, nejspíš jste ve vsyvětlování stejně špatní jako já - učte se to, jinou radu nemám ;)

Změna týmu (vítej v pekle!)

Fáze 3: Nedůvěřivá polopodpora

Po pár měsících mě přesunuli do jiného týmu na jiný projekt, kterému nikdo z programátorů nechtěl velet.
"Unit testy? Co to je? To si nikdo neobjednal!" - první reakce na otázku automatických testů. Refaktoring mi byl opět zakázán a opakovaně jsme si s projekťákem vysvětlovali, proč ano a proč ne - nicméně nikdy jsem s tím nepřestal. Testy jsme ale nepsali ještě dlouho.
Můj první úkol byla "opravička" (podle zadání droboučká) v aplikaci, která měla asi 5000 řádek ve 4 třídách (jedna měla přes 3000 řádek). To snad ani nebyla Java, to bylo peklo. Dostal jsem na to 12 člověkodní. Po dopoledni, stráveném "pícháním klackem" do zdrojáků, jsem pochopil, že netuším, co to dělá a jak.
Za nějakých 5 člověkodní jsem z těch 4 tříd udělal 42, aniž by se aplikace chovala jinak. Jen víc psala do logů. Později popíšu detailně, jak na to ;)
Za další den jsem našel hledanou chybu. A cestou našel asi 4 další, které stoprocentně uživatel musel vidět také!
Při ručním testování se zjistilo, že analytici našli další chyby, a že jsou i v provozní staré verzi. Po konzultaci se zákazníkem došlo i na opravy těchto chyb, načež po 7 člověkodnech jsem byl hotov (později vysvětlím, jak se to pozná ;)).

Fáze 4: Poplácání po zádech ... a ... a jéje

Ano, to byla malá výhra. A protože jste "fakt dobří":
1) Příště máte času polovičku, ne-li mnohem méně
2) Dostanete na starost ostatní programátory
3) Nekonečné konflikty se všemi, jen vedoucí sousedního týmu se vám směje
4) Přibývá stresu, ubývá chuť k práci
5) Není čas na změny

Tohle je začátek konce, kdy nakonec buď odejdete, nebo dospějete a postavíte se definitivně na vlastní nohy. Opět jsme u argumentace - té se nikdy nezbavíte!

Odhad pracnosti

S oblibou přirovnávám práci programátora k luštění křížovek - hodí se to jak pro vysvětlování věcí projekťákům, tak laikům.
Baví vás to? Tak si představte, že 8 hodin denně, 5 dní v týdnu, luštíte křížovky. Máte pocit, že vás to bude bavit? Ne, programátor vážně není "dělník, co jen pracuje hlavou"!
Takže, zkuste to takhle - šéfovi přineste libovolnou těžší křížovku, a za 5 minut se ho zeptejte, kdy jí už bude mít hotovou. A pak jemně konstatujte, že když on po vás chce odhad, je to podobné.
Pokud je navíc kód ve stavu, kdy se v něm nelze vyznat, je odhad prakticky nemožný. Nenaděláte nic, neexistuje řešení, jak dát byť řádově přesný odhad. Ano, měsíc je vhodná jednotka na jakoukoliv změnu v takovém kódu!

Programátoři

To znáte z amerických filmů, typicky vojenských, sportovních. Ne, nejsou to blbci, naopak! Někteří jsou chytří až moc - což je nejhorší varianta, pokud si je nezískáte na svou stranu. Oni se ve svém kódu přece vyznají a funguje! Nic je nedonutí změnit přístup, formátování, přestat psát Javu ve Vimu, používat na všechno statické metody s názvem a() a 20 parametry nebo hashmapou (vede se věčný boj, co z toho je horší).
Krom věčného vysvětlování a kontrolování cizí práce musíte stíhat i své vlastní programování. Ne, nemůžete přestat programovat - jednak by tým přišel o (aspoň jakýstakýs) vzor, jednak je to váš způsob získat si nějakou autoritu, ale hlavně se pořád ještě učíte nové věci - nemůžete radit a dohlížet na jiné, když sami nevíte, jak na to.
Ano, je to přesně jako v těch amerických filmech: hledejte silné stránky, buďte tolerantní, ale zároveň někdy musíte prostě někoho seřvat, nedá se nic dělat. Pravda je, že až tady mě naučili se "nasrat", do té doby jsem byl flegmatikem.

Konflikty

Z předchozího odstavce už je jasné, že buď najdete alespoň nějaké spojence, nebo odejdete. Nebo zatnete zuby a budete makat, dokud všechno nepřijde samo.
Jako vedoucí jsem byl tehdy řekl bych dost špatný. Jako programátor jsem dělal velké pokroky, ale nově nabyté zkušenosti a informace jsem neměl dost zažité na to, abych je někomu dokázal předávat.
Zpětně vím, že mám vážně moc dobré šéfy. I když se někdy hádáme, odsekávám, jednou jsem projekťáka dokonce nazval diletantem, načež on se neurazil, počkal, až "vyšumím", já se omluvil, a argumentoval a argumentoval, než jsme se nějak dobrali k tomu, že "doufá, že vím, co dělám".
Zkrátka, pokud víte, že jste něco přehnali, nebo dokonce byli na někoho nespravedliví, řešte to co nejdřív a velkoryse.

Stres

S tím mám problém dodnes. Je toho moc. Rady už znám taky, ale neumím se jimi důsledně řídit: vstávejte rituálně, pravidelně a stejně pravidelně choďte z práce. V práci nezapínejte Facebook ani jiné podobné blbiny, žerou neuvěřitelné množství času a oberou vás buď o výsledky, nebo o zbytek dne, kdy ty výsledky budete dohánět. Dodržujte pitný režim a jezte střídmě. Po práci se fyzicky hýbejte.
Tož jsem si to teď hezky sepsal, třeba se konečně polepším :-)
Chuť k práci mám, tu mi dává pohled zpátky na to, co všechno se už změnilo k lepšímu.

Změny

Z pohledu projekťáka, natož zákazníka, není na výslovně zákazníkem neobjednané změny čas nikdy.
Takže pracujete tak trochu v ilegalitě. Opět, jste experti, musíte vědět, co můžete změnit a co nemůžete změnit v dané iteraci. Prakticky jde o to, aby to neoddálilo termín - a pokud, aby to bylo obhajitelné. Což se lehko říká, ale těžko dělá.
Řekněme, že do toho budete dávat zhruba 30% času týmu. Pro projekťáka je to strašně moc a bude to chtít snížit. Nebo aspoň pro tuhle iteraci. Ne, neexistuje. Proč? Protože pokud to neuděláte TEĎ, termín bude možná (!) splněn, ale:
1) Větší riziko (řekněme rovnou, jistota) chyb, které tým bude řešit zdarma v rámci záruky
2) Oddálení údržby neznamená, že příště jí strávíte 60% další iterace, ale zůstane tam těch 30% a mezitím vám přibude produkce z minulé iterace.
3) Nezapomínejme na lidskou paměť - o dvě iterace dál už se učíte chápat svůj vlastní kód, čili stojí to další čas navíc.

Pokud změny uděláte, má to "nenápadné" bonusy:
1) Odhad pracnosti je řádově přesnější (z měsíců jdeme na dny)
2) Opravy jsou záležitostí hodin, dokonce minut
3) Od nějaké úrovně stavu kódu lze levně psát unit testy
4) Rozvoj aplikace má tah na branku, víte, co děláte a co ještě budete dělat, neobjevují se záhadné chyby.

Dnes

Fáze 5: Mám nejlepší tým v historii projektu

Ne, pořád jsme nevyhráli. Ale vedeme si dobře. Ta věta z podnadpisu mě napadla chvíli poté, co jsem se na jednoho "mého" programátora ukrutně naštval, seřval ho a za chvíli mi došlo, že neprávem. A že jsem poslední dobou dost nepříjemný. A že to není poprvé. Obratem jsem se mu omluvil a na další poradu přinesl láhev vína a nešetřil jsem.
Stresu je hodně, ale to není žádná omluva, takhle by se ten tým rychle rozložil. Vyhrazuji si právo být člověkem a občas se zachovat jako idiot, načež to ale neznamená, že bych tím nenáviděným idiotem hodlal zůstat.

Zpětně jsme udělali ohromný kus práce s tou půlmilionřádkovou saní. Ano, pořád půlmilionřádkovou, počet řádek narůstá velmi pomalu, protože pořád je tu těch 30% investice (někdy méně, někdy více). Mezitím se aplikace naučila komunikovat s asi 6 dalšími systémy, přešla z Javy 1.4 na JDK6 a má našlápnuto na JDK8, bylo napsáno asi 3000 testů od knihoven až po aplikace a testy GUI, přešlo se z Antu na Maven2 a později Maven3, kvalitu nám už několik let "známkuje" Sonar, máme automatický build systém a zasílání změn, atd.

V jednu dobu bylo na projektu neuvěřitelných 13 lidí. Dnes je nás 5 a děláme toho snad ještě více.
Náš projekťák má občas pořád tendenci někde "ušetřit", ale nechá si to vysvětlit a nepochybně svou práci dělá dobře.

Dostávám nabídky na dvojnásobek současné mzdy, ale není to jen tak - neodcházím z několika rovnocenných důvodů:
1) Mám rád evoluce, ne revoluce. Chci si užít pocit, kdy už budu vědět, že všechno klape. Myslím, že nejsem tak daleko od tohoto stavu.
2) Jiná práce, jiný tým, jiné podmínky, dost možná bych musel znovu někoho přesvědčovat, že používá technologie, ke kterým už se dnes nikdo nezná, že psát testy není ztráta času, že požadovat nějakou úroveň zdrojáků není perfekcionalismus, ale pud sebezáchovy.
3) Mám rád živé projekty, ne režim "přijít - napsat - spustit - odejít".
4) Mám tým, ve kterém se dokážeme domluvit, vyhovět si, a případně i zabojovat, aby projekt dopadl dobře (zákazník spokojený, projekťák spokojený, firma dostane zaplaceno a my možná odměny :-) ).

Samozřejmě to neznamená, že tu ta možnost odejít není. Ale momentálně jí nevyhledávám a nabídky odmítám.

Závěr úvodu (no hurá)

O refaktoringu se tu vlastně dneska mluvilo málo, zato dlouho. Asi jsem nenapsal ani nic, co by člověk nevěděl, ale šlo mi o to nabídnout pohled na tu "evoluci" od začátku do konce (budiž prozatímním neustále utíkajícím koncem označen dnešní den).
Pokud přijdete na projekt o této velikosti, nečekejte, že s ním pohnete jen tak. Je to práce na dlouho a vy se pořád musíte vejít do rozpočtů a těžce si vydobývat důvěru jiných. Ta vzniká tak, že uspějete krůček po krůčku v zakázkách, že vaše dílo funguje, když jiná mají problémy.
Je to hodně o hledání rovnováhy a schopnosti dotahovat věci do konce tak, abyste se k nim nemuseli vracet nečekaně a často a ... neradi.

Ano, zákazník srovnává hrušky s jablky, a vy to děláte taky, jen si to neuvědomujete. Dnešní naší společnosti chybí respekt, empatie, schopnost podívat se na něco očima někoho jiného - ale o tom jindy a jinde. Učte se to. Většina firem nabírá programátory a analytiky v jednom, takže - analyzujte ;)

Další díly asi budou kratší a jelikož to píšu bez přípravy a konceptu, uvidíme, co ze mě "vypadne".  Každopádně bych se chtěl zaměřit na nějaké strategie refaktoringu a zhodnocení toho, která kdy může být úspěšná.

Poslední dnešní věta:
Neexistuje nerefaktorovatelný kód!