Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Reaktivní programování

| Moje práce

V uplynulém roce se začaly hejbat ledy okolo rektivních extenzí a reaktivního programování vůbec. A já byl u toho. :)

Reaktivní manifest

Byl publikován Reaktivní manifest, který se snaží definovat, jak mají reaktivní aplikace vypadat a proč.

  1. Mají reagovat na události
  2. Mají reagovat na zátěž
  3. Mají reagovat na selhání
  4. Mají reagovat na uživatele

Manifest vyjadřuje moje snažení v uplynulých letech, takže jsem ho samozřejmě signoval.

CZPodcast

Již potřetí jsem byl pozván do CZPodcastu, abychom se pobavili o reaktivním programování, reaktivním manifestu a reaktivních extenzích.

CZ Podcast 92 – Reaktivní programování by Aleš Roubíček on Mixcloud

DevFest Pardubice

Na DevFestu jsem měl přednášku o Reaktivním programování a reaktivních extenzích.

Autor: Aleš Roubíček |

Mikroslužby

| Moje práce

Máme tu jedinečnou příležitost definovat si Mikroslužby ne v tom, co nejsou, ale v tom co jsou.

Tohle je má definice.

Definice

Mikroslužba je oddělený izolovaný a pojmenovaný kus logiky, který konzumuje 0..N vstupů a produkuje 0..N výstupů, který je vykonaný za účelem přinést konzumentovi nějakou výhodu. Toto je poskytováno jako služba.

Oddělená

Důvodem, proč to nazýváme Mikroslužbou a ne Službou, není počet řádek, který je menší než N, nebo velikost nasazované služby, která je maximálně N kilobajtů nebo žádná jiná libovolná metrika. Tím důvodem je nízký počet zodpovědností: jedna. Dělá to jen jednu věc a tu dělá skvěle.

Izolovaná

Izolovaná znamená, že funguje odděleně od ostatních věcí, což z ní dělá distribuovanou entitu. Nezáleží na tom, kde konkrétně leží fyzický hardware nebo na jakém OS to běží. Pokud to spadne, zůstává to v izolaci. Předchází se tak kaskádovému šíření chyby. Navíc nám to umožňuje snadno vyvíjet a aktualizovat službu samostatně.

Pojmenovaná

Jelikož jsou Mikroslužby izolované, potřebujeme na ně nějakým způsobem odkazovat. Jméno v kombinaci se vstupy a výstupy Mikroslužby nám definují signaturu nebo identifikátor Mikroslužby.

Vstupy a výstupy

Pokud máme jazyk, který podporuje typové informace, vstupy a výstupy mohou být typové. Tyto typy by měly být reflektovány při manipulaci. Což znamená, že konzument služby by měl znát, co služba přijímá a co poskytuje.

Vykonání

Protože konzument není ten, kdo vykonává logiku Mikroslužby, a Mikroslužby jsou přirozeně distribuované, volání nesmí konzumenta blokovat ve vykonávání jiných operací po dobu výkonu mikroslužby. Konzument nesmí být držen jako rukojmí, dokud mikroslužba nevyprodukuje svůj výstup. To znamená, že musejí být vytvořeny takové abstrakce, které nám umožní Mikroslužby konzumovat asynchronně.

Překlad článku Microservices od Viktora Klanga

Autor: Aleš Roubíček |

Legacy code writer - part 2

| Moje práce

Dostal jsi nový úkol, implementovat jednoduchou featuru do korporátního projektu. Jenže ten kód byl past vedle pasti.

Produkoval jsi legacy kód, kvůli Legacy code writerovi.

Nikdo nepíše legacy code od základu

Je dost možný, že vám první díl zněl trochu povědomně. Každý z nás se dost pravděpodobně v podobné situaci ocitl.

Nyní, prosím, zkuste přehodnotit svou víru ve zlého Legacy code writera. Myslíte si, že opravdu existuje? Jste si jistí, že pokaždé když narazíte na legacy code, tak k němu existuje mystický můž, který to celé napsal od základu?

Víte čemu věřím já? Že tento muž vůbec neexistuje, že je to jen velice příjemná a pohodlná zástěrka. Není tu žádná taková osoba, která by byla zodpovědná za celej ten bordel. Špatný programátor, Legacy code writer, je jen fiktivní postavou vytvořenou k tomu, aby nám pomohl obhájit naše malé, přetrvávající, hříchy.

Došel jsem k názoru, že psaní legacy code je distribuovanou aktivitou. Každý z nás do tohoto vleklého procesu, třeba i nevědomky, přispívá.

Zbytečně nezahazujte čas pátráním, kdo za to může, protože to my všichni. Každý z nás!

Legacy code je jako prach

Pokud neuklízíte, neustále se usazuje a je jedno jestli se díváte nebo ne.

Každý kus kódu, pokud je spravován více lidmi, tíhne k tomu stávat se špagetami nebo lasagněmi. Entropie má potřebu růst. Je to absolutně přirozený proces.

Nevěříte?

Zkuste se na to podívat z jiného úhlu pohledu: Kód přirozeně netíhne k tomu být čím dál tím lepší pouhým přidáváním featur hordou programátorů.

Mně to zní jako jednoduchý přírodní zákon: Živý kód tíhne k tomu, stávat se horším, dokud nedojde k záměrnému aplikování síly proti přirozené potřebě entropie růst.

Legacy code je to, co získáte, pokud nejste odhodlaný přísně aplikovat dobré zásady při správě kódu.

Dvě špatná rozhodnutí se vzájemně nekompenzují. Komulují!

První verze byla pravděpodobně navržená dobře. Šlo to celkem snadno, před programátorem byl list nepopsaného papíru. :)

Každý dokáže navrhout dobrý kód na zelený louce a udržovat ho po dobu tří měsíců. Pouze profesionální vývojář to zvládne i po dobu následujících let.

Nekritizujte prvního progrmátora, on možná odvedl velmi slušnou práci. Jenže jak na chvilku polevíte, začnou se objevovat první nepatrné částečky legacy kódu. Problém s částečkama legacy kódu je jako s korálama: mají potřebu se na sebe lepit až z nich nakonec vznikne nepropustná bariera pevná jako skála, se kterou už nepohnete.

Zaveďte dočasnou proměnnou a vězte, že další programátor se bude cítit oprávněn na ní nalepit nějakou funkcionalitu. Udělejte copy&paste maličké pětiřádkové funkcionality a čekejte že vaše budoucí já v jedné z těch kopií změní dva řádky a připraví tak jeho nástupce, kteří se to pokusí refactorvat, o několik vlasů. Nejspíš to vzdají.

Vynechte unit test a vězte, že další team si pomyslí:

Předchozí programátor si myslel, že pokrýt tuhle funkcionalitu testy je příliš nákladné. No, teď to určitě nebude o nic snažší. Takže se na to taky vyprdneme.

A tady to začne chátrat. Taky se tomu taky říká teorie rozbitého okna

Autor: Aleš Roubíček |

Legacy code writer - part 1

| Moje práce

Dostal jsi nový úkol. Zadání zní jasně:

Přidat jednoduchou featuru do korporátního projektu.

Sice o tom nic nevíš, ale požadavky zní rozumně a jednoduše. To přece zvládneš.

Nebo si to aspoň myslíš. Dokud neotevřeš ty zpropadený zdrojáky.

Nehoráznej bordel. Který prase tohle mohlo napsat? Na monitoru vidíš jen změť špaget a lasagní. Ach. Vypadá to, jako by někdo záměrně vytvořil sbírku všech známých anti-patternů.

Vztek roste, pomalu začínáš nenávidět toho, kdo tohle zvěrstvo způsobil. Tahle hromada sraček ti nedovoluje pracovat tak, jak ti tvé svědomí káže a jak bys opravdu chtěl. Chceš přece dělat dobrý řemeslo, držet se oblíbených vzorů. Sám sobě jsi slíbil, že budeš psát kód s vysokou kohezí, volnými vazbami a ty sám nejlíp víš s čim… Ale zkrátka to není možný.

Jak taky? Ten neschopnej blbec všude používá globální proměnné, skoro všechno je statický, kód se na mnoha místech opakuje a navíc nikde žádný testy!

A hele, tady dokonce používá magický konstanty!

int HRW_UND = 12;

Co sakra může ta dvanáctka znamenat? A co je to HRW_UND? Uff. Ať schoří v pekle, ten bastard!

Tak. Nemáš moc času, deadline se neúprosně blíží a tys měl přece jen přidat jednoduchou featuru a ne refactorovat celé to boží dopuštění. To přece není tvoje práce! Ne asi.

Legacy kód vždycky vyhraje nad čistým kódem

Tak jo, ten dobrák, co udělal tuhle spoušť vyhrál. Přidáš tam tu featuru bez striktního dodržování svých zásad. Proč ne? Vždyť ty za tenhle bordel přece nemůžeš, tys ho neudělal, musíš akorát dokončit svůj úkol. O to tu jde.

Byl jsi donucenej dělat pod svoje možnosti. Ale není to tvoje chyba. Máš dobré důvody, vždyť je to přece chyba autora legacy kódu.

Tady bys chtěl předat závislost pomocí dependency injection, ale úsilí překopat celou tuhle šílenou věc by několikrát překročilo rámec tvé featury. Všechno je statický, nikde žádná rozhraní, brutální dědičnost a spousta výskytů operátoru new… Nemáš čas a ta šílená code base ti vůbec nepomáhá. Srát na Inversion of Control!

Radši přidáš statickou metodu do service locatoru. Sice nenávidíš service locator, ale je prolezlej úplně všude a nejde se ho lehce zbavit.

A tady je zas metoda, která řeší dvě různý věci najednou. Ty bys potřeboval jen jednu, ale celý je to tak propletený. Je to pěkně těžký oddělit tyhle dvě různý věci od sebe, protože ta třída ani nejde otestovat. Nakonec radši zkopíruješ tu část kódu, kterou potřebuješ a vložíš ji tam, kde se ti bude hodit.

Hej! A co tahle 50 řádková metoda s 18 ifama? Proč ten vůl nepoužil strategii? Refactorovat celý to zvěrstvo, nebo přidat 19. if? :) Tak určitě! Ať původní autor shoří v pekle!

Konečně. Teď ještě potřebuješ konfigurační parametr. Normálně bys to poslal přes property injection, ale původní autor použil HRW_UND = 12, tak tam prdneš HRW_UND = 98.

Je to jeho vina.

Autor: Aleš Roubíček |

Jak teda s těma výjimkama v busines logice?

| Moje práce

Na develu se rozvíjí debaty o tom, zda je zpátečnické odrazovat od vyhazování výjimek. Já patřím mezi zpátečníky. :) Tady je moje odpovědˇ, kterou je IMO škoda nemít veřejně dostupnou, takže ji publikuju i tady na blogu.

Na začátek je dobré se opřít o nějakou autoritu, proto tady pro začátek ocituju .net Framework Design Guidelines doporučení k výjimkám:

  1. Nevracejte chybové kódy. K tomuto účelu jsou určeny výjimky.
  2. Vyhazujte výjimky pokud dojde k běhové chybě. Pokud člen nemůže úspěšně provést úkol za nějž nese zodpovědnost, mělo by to být považováno za běhovou chybu a měla by být vyhozena výjimka.
  3. Pokud se dostanete do stavu, kterému nepomůže žádné ošetření výjimky, proveďtě ukončení procesu pomocí Environment.Fa­ilFast().
  4. Nepoužívejte výjimky k formálnímu řízení toku programu. S výjimkou systémových selhání by vždy měl existovat způsob jak se vyhnout vyhazování výjimek.
  5. Zvažte důsledky vyhozené výjimky na výkon.
  6. Vždy zdokumentujte všechny výjimky vyhazované veřejnými členy z důvodu porušení jejich kontraktu.
  7. Nemějte veřejné členy, které mají nebo nemají vyhazovat výjimky na základě nějakého parametru. Type GetType(string name, bool throwOnError)
  8. Nemějte veřejné členy které vracejí výjimku jako návratovou hodnotu nebo out parametr.
  9. Nastavte všechny relevatní vlastnoti vyhazované výjimky.
  10. Preferujte užití systémových výjimek před vlastními.
  11. Vlastní výjimky vyhazujte v případě, že vyžadují odlišný způsob ošetření než existující typy výjimek.
  12. Nikdy nevytvářejte vlastní typ výjimky, jen proto, aby váš team měl nějakou vlastní výjimku.
  13. Vyhazujte co nejkonkrétnější výjimky.
  14. Nevracejte chybové kódy z obav o výkonostní dopady při vyhazování výjimek.
  15. Používejte vzory jako TryParse, které snižují negativní dopady na výkon způsobené vyhazováním výjimek.

Z texu je zřejmé, že vyhazování výjimek má své opodstatnění. Důležité je však v jakých scénářích se výjimky vyhazovat mají a v jakých ne.

Často se tu objevují příklady s validací vstupních hodnot nebo třeba autentikace uživatele. Začněme validátorem:

Primární zodpovědností validátoru je validovat hodnoty. Je nevalidní hodnota příčinou nemožnosti korektní práce validátoru (důvodem k vyhození výjimky)? Není (viz bod 2). Můžeme to řešit vracením primitivy jako je bool. Já si však myslím, že taková hodnota je nedostatečná. Potřebujeme výsledek validace zpropagovat někam, kde se s ním bude dále pracovat. Vyhodíme tedy výjimku a tu pak odchytíme? Tím se ale dostáváme k bodu 4. Lepším řešením je definovat komplexní návratový typ:

type ValidationResult =
  | Valid
  | Invalid of ValidationError list

Pokračujme s autentikací uživatele. Běžnou praxí je vyhazování SecurityException pokud se nepovede uživatele autentikovat. Výjimka se pak nechá probublat do UI vrstvy, kde se ošetří a uživateli se zobrazí hláška, že zadal špatné jméno nebo heslo. Opět však jde o porušení 4. bodu. Autentikace tam může vracet opět bool nebo rovnou instanci User a v případě nevydařené autentikace null. Tím se zbavíme bezpečnostních výjimek, ale zase si zavedeme potenciální chybu v podobě NRE… Proto si opět myslím, že by se měl vracet adekvátní typ:

type AuthenticationResult =
  | NotAuthenticated
  | Authenticated of User

Autentikační modul samozřejmě může vyhazovat výjimky, ale většinou budou spojené s nefungující infrastrukturou (LDAP, SQL nebo jinej auth provider), to je totiž validní důvod, proč nemůže autentikace doběhnout.

Autor: Aleš Roubíček |