úterý 20. prosince 2011

CZ Podcast 59 - Automatizace a Configuration management

Díl 59. je venku přátelé, a to je pro tento rok z naší estrády definitivně všechno. Sosejte, poslouchejte a doufam, že to neni jenom mlácení prázdné slámy, jak si z toho občas děláme s Filemonem srandu.

úterý 13. prosince 2011

CZ Podcast 58 - Výuka Informatiky na vysokých školách

Přátelé čekal jsem to horší, že to jde s výukou od desíti k pěti. Nakonec konfrontován s realitou jsem si uvědomil, že úplně totálně v pasti to naše školství není. Poslouchněte si další díl.

neděle 4. prosince 2011

Další krok k Continuous Delivery

V několika předešlých článcích jsem se věnoval continous delivery a ještě ve starším článku jsem se zamýšlel nad buildováním z jedné hromady oproti releasovatelným celkům. Když nad tím teď přemýšlím, přijdou mi releasovatelné celky jako výborný krok k continous delivery, alespoň v našem prostředí v GoodData. V tomhle článku vám nechám trochu nahlédnout do kuchyně toho jakým způsobem vyvíjíme Java komponenty a připojím pár postřehů, které si myslím, že by nám mohli pomoci k plnému continous delivery, protože zatím jsme schopni doručovat nové verze produktu přibližně každých 14 dní.

Na začátku všeho je commit

Chtělo by se začít tímto titulkem, ale já to neudělám, a začnu organizací kódu která se nám osvědčila. Úplně původně jsme měli všechen zdrojový kód na jedné hromadě. V heterogenní aplikaci, kterou v GoodData vytváříme, jsme měli sice strukturované, ale stále na jedné hromadě zdrojové kódy všech služeb UI klientem počínaje a ROLAP enginem konče. Tahle směska má speciální buildovací skript, který volá jednotlivé buildovací nástroje poplatné pro daný jazyk/platformu, v případě Javy to byl Maven. Výsledkem buildovacího procesu jsou RPM balíčky, které umožňují nainstalovat celý produkt.

Ve verzovacím systému pro správu zdrojových souboru (používáme Git) používáme při vývoji nové funkcionality koncept feature branch, to znaméná, že máme Master větev, do které je možné zamergovat z Feature větve až potom co člověk projde prověrkou kvality, která se skládá z důkladné sborky a rozborky, code review a kurately záludných otázek. Merge do Masteru je úzkým hrdlem celého procesu, a code review je docela komplikované ve chvíli kdy měníte různé komonenty. V Jave máme navíc velké množství komponent, které používáme skrze různé služby a proto je potřeba být velice obezřetnými při jednotlivých změnách. Při buildu na jedné hromadě je prakticky nemožné používat stabilní verze komponent a zároveň mít vývojové verze, protože komponenty mají různý vývojový cyklus. Navíc máme odladěné verze komponent, do kterých se prakticky nesahá a nechceme jimi neustále zdržovat kontinuální buildy ať již z důvodů rychlosti či zdrojů.

Releasovatelné komponenty v praxi

Z výše uvedených důvodů jsme se rozhodli postupně migrovat Java komponenty z jedné velké hromady do separátních repositářů na GitHub a releasovat je jako binární artefakty Maven infrastrukturou. Co se týká Javy máme něco kolem třiceti GitHub repositářů. Každý repositář má svého strážce (gatekeeper), tedy člověka který je zodpovědný za komponenty v daném repositáři. Pokud chci něco v dané komponentě změnit udělám to ve vlastní větvi, ze které vytvořím Pull request. GitHub na to má skvělou podporu bez toho aniž bychom si museli cokoliv programovat vlastními silami. Strážce dostane emailovou notifikaci, s odkazem na Pull request. Přes webové rozhraní si prohlédne změny a má dvě možnosti. Pokud je vše v pořádku akceptuje Pull request a GitHub se postará automaticky o zamergování změn. Pokud se strážci něco nezdá, může do zdrojového kódu, případně k Pull request, dopsat svoje komentáře přes webové rozhraní. Ty jsou žadateli o Pull request zaslány formou emailu. Výhrady se zapracují a pokud není jiných námitek Pull request se akceptuje. Tím je pokryté code review.

Nad zdrojovými repositáři na GitHubu máme post commit hooky, které se postarají o klasické kolečko build, test a deploy výsledného artefaktu do binárního Maven repositáře. Jediné bílé místo představuje release management komponenty, který dělá strážce manuálně. Pokud je potřeba vyrealsovat stabilní verzi komponenty musí strážce spustit manuálně Maven release plugin, který se postará o všechny záležitosti (build artefaktu, změna verze POMu, přiřazení tagu atd.). I release komponent bychom chtěli dělat automaticky (pravděpodobně speciánlí commit message a nebo manuálně z CI serveru).

Pravidla

Release notes

Každá komponenta má u sebe soubor RELEASENOTES.md, ve které zaznamenáváme změny v lidsky snadno pochopitelné podobě, aby to člověk nemusel dohledávat z commit logu. Používáme markdown syntaxi, kterou dokáže GitHub renderovat ve webovém rozhraní když člověk prochází repositář.

## 1.1.2
* Added `AbstractMediaTypeAwareFilter` filter provides support for detection  media types from  HTTP headers
* Added `RetainingHttpServletResponse` special `HttpServletResponse` implementation allowing manipulation with the response body and HTTP headers until the flush method is called
* Added `RetainingResponseFilter` this filter wraps `HttpServletResponse` to `RetainingHttpServletResponse`

## 1.1.1
* Added support for altering the Content-Type header (ContentTypeAlteringRequestWrapper)
* Added support for changing the entire request body (BodyAlteringRequestWrapper)   
Verzovací schéma

Používáme klasické trojčíselné označení verze X.Y.Z. Tímto způsobem verzujeme, aby bylo na první pohled patrné, k jakému typu změny v dané verzi komponenty došlo. Číslo X označuje generaci komponenty, je vždy 1 a nepředpokládám, že bude jiné, snad jenom kdybychom se rozhodli danou komponentu totálně překopat. Číslo Y označuje zásadní změny, které mohou být nekompatibilní s předchozí verzí. K nekompatibilním změnám se ještě vrátím. Poslední verze označuje inkrementální změny, bugfixy atd.

Pokud se rozhodnete releasovat komponenty stane se pro vás zpětná kompatibilita velkým zaklínadlem. Nevíte totiž kde kdo v jaké verzi vaší knihovnu používá a kdy se rozhodne nebo případně bude donucen (tranzitivní závislosti) používat novější verzi.

Postupem jsem dospěli k tomu, že nejlepší je dělat zpětně kompatibilní změny především u nízkoúrovňových komponent např. HTTP klient, protože jinak se vám výsledný celek může sesypat jak domeček z karet. Mimochodem to je jedna z nevýhod releasovatelných komponent oproti buildu z jedné hromady. U aplikačních komponent už na to není třeba brát zas takový ohled.

Readme

Většina komponent by u sebe měla mít popis toho k čemu slouží a jak se používají. To usnadňuje orientaci při jejich použití.

JSON
===========
This repository consists of two parts

* [JSON validation](https://confluence.gooddata.com/confluence/display/analysis/JSON+validation+documentation)
* JSON serialization support - also contains [AbstractJsonObject (de)serialization](https://confluence.gooddata.com/confluence/display/Development/Abstract+JSON+object+serialization)

Continous delivery

V Continous delivery alespoň u Java služeb nám brání několik překážek. Zatím nemáme releasovatelné úplně všechny high level služby. Nicméně se držíme pravidla, že v stabilizační větvi (forknutá z Master větve) nepoužíváme SNAPSHOT závislosti. Tím je build reprodukovatelný a nehrozí, že jej rozbijeme. Ve chvíli kdy budeme mít releasovatelné všechny služby už bude jenom několik kroků ke kýženému cíli. Předně psychologická zábrana, které říkám suma všech strachů. Mít tolik testů, schválně neuvádím procento code coverage, které mi umožní udělat commit s vědomím, že případný problém bude zachycen.

Další věcí je staging repository pro binární artefakty služeb. Po tom co je artifakt připravený v Maven repository a tedy úspěšně proběhly jednotkové a integrační testy je potřeba ho otestovat se zbytkem systému. Pokud tyto testy projdou, je možné artefakt posunout do staging repository, ze které je možné brát artefakty pro finální distribuci, která jde k manuálnímu verifikaci.

No a poslední překážkou je neschopnost částečného releasu/rollback jenom některých částí. Zatím dokážeme releasovat jeden velký celek, což samozřejmě zvyšuje náklady spojené s celým releasem.

Závěr

Cesta ke Continous delivery je dlouhá a trnitá, navíc nikdo vám nedá plánek. Funguje jenom postupné zlepšování a učení z vlastních chyb. Doufám, že za půl roku se budu moci pochlubit jak jsme pokročili.

úterý 22. listopadu 2011

CZ Podcast 57 - Pokec s Alešem Roubíčkem o Windows Azure, Code retreat a Agile vývoji

Další díl s Alešem Roubíčkem. Windows Azure mě docela mile překvapil. To se ovšem nedá říci o zvuku, který jsme zachytili mikrofónem. Zakouzlil jsem v Audacity a doufam, že se vám to bude líbit.

sobota 19. listopadu 2011

Postřehy z Devoxx 2011

S kolegy z GoodData jsme vyrazili na konferenci Devoxx, a musím říci předeslat, že to stálo za to. Devoxx je určitě nejlepší konference co se týká Javy v Evropě, která se konala letos po desáté v Antverpách. Paralelně běželo od půl desáté ráno do sedmi večer sedm prezentačních slotů, ze kterých si člověk mohl vybrat to co ho zajímalo. Přestože je to Java konference, byla v nabídce poměrně pestrá škála prezentací z přidružených jazyků a technologií postavených kolem Java Virtual Machine (JVM). Osobně jsem se snažil vybírat prezentace, které neměly s Javou moc společného a to ze dvou důvodů. Jednak nesdílím názor, že postupné nafukování jazyka je cesta, kterou by měly jít další verze Javy. Jednak razím tezi, že člověk by měl chodit na prezentace o technologiích a postupech, které nezná, protože ty ho nejvíce obohatí. Za celou dobu jsem proto z Javy slyšel jenom pár útržků na různých prezentacích.

Clojure

Poprvé v životě jsem viděl Lisp, tedy konkrétně Clojure v dvou prezentacích Alexe Millera. Přiznám se, že syntaxe jazyka pro mě byla natolik exotická, že jsem většinu slajdů v rámci zachování duševního zdraví musel vypustit. Pár postřehů, které jsem si udělal: Lisp dialekt, funkcionální jazyk, kompilovatelný do bajt kódu JVM, imutabilní struktury, kód jsou data, žádné IDE ale Emacs. Síla Clojure se ukázala v prezentaci Stream Execution with Clojure and Fork/Join. V něm bylo prezentováno použití Clojure ve firmě revelytix.com, která umožňuje vyhledávání SPARQL jazykem v RDF dokumentech uložených v relačních databázích. Funguje to asi následujícím způsobem. HTTP rozhraní na které posíláte SPARQL dotazy, z nich se postaví AST a na jeho základě exekuční plán, ten se zoptimalizuje a převede na SQL příkazy, které jsou paralelně vykonány a jejich výsledek sloučen dohromady a vrácen jako odpověď. To všechno prosím pěkně na plus mínus 3000 řádcích kódu Clojure. To na mě udělalo velký dojem, přestože bych spíš pochopil básničky v jazyce Elfů než zdrojový kód, který se tam párkrát objevil.

Continuous Delivery

Každá změna ve verzovacím systému je automaticky přetavena v novou verzi produktu a může se objevit na produkci. Člověk je skoro v pokušení si zaťukat na čelo a jít o dům dál, ale ono to skutečně funguje. Prezentaci měl Dave Farley autor stejnojmenné knihy. Výše zmíněný postup přetavili do praxe ve firmě LMAX, což není garážová firmička, ale pořádný enterprise bumbrlíček, který se živí zprostředkováním finančních transakcí. Kromě toho co jsem psal dříve v článku Continuous Deployment (pozn: ve skutečnosti vaším cílem není deployovat novou verzi, ale doručovat nějakou hodnotu vašim zákazníkům) bych vypíchnul jednu zásadní myšlenku, která zazněla. Pokud chcete někdy kontinuálně doručovat váš software, musíte mít spolehlivý release proces na každé úrovni s dovětkem, že cokoliv co je závislé na zásahu člověka se považuje za nespolehlivé. Více 8 Principles of Continuous Delivery.

HTML 5

HTML 5 byla oblast, kterou jsem v minulosti totálně ignoroval. Musím se přiznat, že HTML 5 pro mě bylo asi největším drahokamem který jsem v Antverpách objevil Někdo použil větu HTML 5 is a game changer a já s tím musím souhlasit, protože nabízí jednu vývojovou platformu pro svět mobilních telefonů, tabletů a osobních počítačů. Ve všech prezentacích zaznělo kolik má problémů z některých jmenujme: nekompatibilita prohlížečů, chybějící nástroje, výkonnost oproti nativním aplikacím, na druhou stranu nabízí obrovský potenciál. Z technologického hlediska mě zaujala jedna zásadní myšlenka. HTML 5 umožňuje (nikoliv znamená!) změnu architektury webových aplikací. Díky HTML 5 si můžeme dovolit napsat aplikaci, která nebude přímo závislá na online připojení k serveru a bude si dokonce moci držet stav.

Cloud a Java

Viděl jsem keynote věnovanou Java EE, ve které byl hlavním tématem cloud. Původně jsem chtěl napsat, že to jak si cloud a aplikace v něm napsané představují chlpaci z Oracle, a jak si jej představují já, jsou dva naprosto odlišné světy. Nakonec jsem dospěl k tomu, že moje představa je pokřivená mými zkušenostmi a agilitou potřeb firmy, pro kterou pracuji. Java EE je opravdu úsilí, které je zaměřeno na dodání standardů pupkatým pánům v korporacích. Když to akceptujete, bude váše chápání Enterprise Javy mnohem méně kritické. Proto snahu o podporu JSONu, JPA nad NoSQL databázemi v další verzi berete s jemným přimhouřením oka. To co nejvíce kontrastovalo s mým chápáním, a nebo to na mě přinejmenším tak působilo, byla vize homogenního cloudu z pohledu hostovaných aplikací a služeb. V případě Java EE to byl EAR/WAR v aplikačním serveru. To si myslím může platit pouze pro určitou skupinu aplikací v určitém typu prostředí a napadají mě opět pupkatí páni... Pokud nemáte jenom EARy, WARy a nebo se jinak odlišujete od představy cloudu, například technologiemi které používáte, už pro vás Java EE koulí na noze.

S tím kontrastovaly prezentace Platform As A Service poskytovatelů, kterým byli Heroku a Cloud Foundry. Přestože by se mohlo zdát, že Java EE a tyto dvě služby k sobě mají stejně daleko, jako Řekové k vyrovnanému státnímu rozpočtu, opak je pravdou. V obou službách totiž přesně pochopili, že vývoj dnešních aplikací se neodehrává v jednom jazyce a za použití jedné technologie. Speciálně bych se zastavil u Cloud Foundry, jehož myšlenka mi přijde skvělá. Skrytý problém dnešního cloud computingu se nazývá vendor lock-in. Česky by se to dalo přeložit jako přikování k jednomu dodavateli. Když si dneska postavíte svojí aplikaci nad Amazonem, budete více či méně svázáni s jeho infrastrukturou a migrace na jiného poskytovatele bude velmi bolestivá. Cloud Foundry by se dala popsat jako prostředník mezi vaší aplikací a poskytovatelem cloud. Namísto toho, abyste aplikaci a nástroje pro monitoring a správu stavěli proti konkrétním službám daného poskytovatele, stavíte je proti API Cloud Foundry, které je adaptuje na konkrétní podvozek. Přechod z jednoho poskytovatele na dalšího pak není tolik bolestivý. Za Cloud Foundry stojí VMware, ale projekt jako takový je open source, tento model bych přirovnal k linuxovým distribucím, jako příklad bych to ilustroval na Stackato.

Závěr

Zaujal mě framework Disruptor pro pekelně rychlé předávání dat mezi dvěma vlákny, když si ovšem přečtete jejich tech paper zjistíte, že máte k dispozici asynchronní event framework. Zaujaly mě rovněž Remote Actory v Akka 2.0 viz strana 27 a Play framework, který kompletně obchází Servlet API a je postavený právě kolem konceptu Actorů. Naopak mě zklamala keynote Tim Braye, ze které jsem si odnesl pouze odvážnou myšlenku, že trh pro aplikace na mobilní zařízení leží v rozvojových zemích. Pocit politicky korektního, rozuměj nic neříkajícího, tlachání jsem měl z diskuzního panelu kde byli mimo jiné pánové jako Joshua Bloch, Brian Goetz, Ben Evans a Mark Reinhold. Buďto všichni právě střízlivěli z párty předešlého dne a nebo tomu chybělo lepší moderování a výběr otázek. Jsem v pokušení napsat, že Filemon by to zvládnul lépe než Tor Norbye. Nikdo z nich v podstatě nepřinesl na stůl nic nového a především se snažili hlavně někoho neurazit.

To byl Devoxx 2011 mýma očima.

Mimochodem doporučuji i postřehy Lukáše Křečana, které jsou vtipnější než ty moje a navíc jsme navštěvovali až na pár vyjímek různé prezentace.

pátek 11. listopadu 2011

MongoDB - komentář k posledním událostem

V posledním týdnu se po různých diskusích začalo trousit hodně negativní reklamy na MongoDB. Jedním z těch příkladů je anonymní Don't use MongoDB s odpovědí přímo od 10gen. Samozřejmě konkurence si taky přihřála polívčičku ačkoliv to později uvedla na pravou míru. Kdybych to měl shrnout MongoDB je designované s určitými vlastnostmi a vy je buďto akceptujete a budete s nimi počítat a nebo budete nemile překvapeni.

Prvním poměrně zásadním designovým rozhodnutím bylo vypnutí (default) potvrzovacího módu pro write operace. To má za následek, že sice z vašeho pohledu zapíšete data, ale to neznamená, že se data byla ve skutečnosti zapsána na disk. Důvod k tomuto rozhodnutí je přesvědčení autorů, že ne všechny zápisu musí být kritické a zdržovat klienta. Pokud například používáte MongoDB pro sběr logovacích událostí tak vás pravděpodobně nebude mrzet, že některé z nich nebudou za určité konstelace trvale zapsány. Každopádně, a to je důležité zdůraznit, potvrzení zápisu můžete ovládat klientem a to do té míry, že můžete například vynutit zápis na určitý počet replikačních instancí Mongo v clusteru.

Dalším poměrně často kritizovaným aspektem MongoDB designu byla možná korupce datového souboru při pádu serveru. MongoDB používá mapované soubory do paměti a jednou za čas udělá fsync, kterým se zapíše aktuální stav na disk. Může dojít k porušení integrity pokud ustřelíte MongoDB v ten pravý okamžik, potom vám nezbývá nic jiného než pustit recovery. Od verze 1.6 můžete použít žurnál a máte vystaráno. To má sice negativní implikace na výkon zápisových operací, ale v nereplikovaném řešení je to jediná možnost. Pokud provozujete MongoDB v master/slave módu a nebo replica setu, můžete nechat sesynchronizovat porušený node z masteru či dalších replikantů a máte po problému (teorie - nezkošuel jsem zatím).

Posledním designovým rozhodnutím, které bývá často kritizované, je globální read/write zámek. Díky němu si nikdo jiný neškrtne pokud běží zápisová operace. To je docela nemilé, ale... Záleží kolik zápisových operací máte vzhledem ke čtení. Moje domněnka je, že rozhodnutí použít globální zámek udělali, protože je to nejjednodušší způsob, jak vyřešit zamykání dat bez zvýšení paměťové náročnosti (nevýhoda MVCC). Každou novou verzi databáze se optimalizuje výkonnost zkracováním kritické cesty, aby se zámek držel pokud možno co možná nejkratší dobu.

Toliko k nejčastěji uváděným "nedostatkům" nebo designových rysům MongoDB. Celá ta diskuze mi velmi připomíná debatu Ruby on Rails versus zbytek světa asi před třemi lety: "No vy to sice umíte krásně, ale to se nedá použít, protože požadavky se odbavují jeden po druhém. To nemůže nikdo nikdy použít pro reálný projekt". Samozřejmě kdo chce psa bíti hůl si vždycky najde. V tomhle případě se krásně ukázalo, že pokud existuje technické řešení, pak to není vůbec problém. Pokud bych to převedl zpět na MongoDB, firma 10gen si všechno velmi dobře uvědomuje a velmi se snaží všechny defekty řešit a databázi dál vylepšovat.

Nástup NoSQL řešení je změnou paradigmatu, kterým jsme vyvíjeli webové aplikace posledních deset let. Relační databáze jsou nahrazovány alternativními úložišti z různých důvodů, které jsou dostatečně známé. Ten kdo dneska pošilhává po NoSQL řešení by neměl počítat s tím, že dostane hotový produkt typu relační databáze, jinak bude dost zklamaný. Pokud hodláte MongoDB použít mějte všechny tyto věci na paměti.

pondělí 7. listopadu 2011

CZ Podcast 56 - Cloud computing, Amazon AWS

56. díl jsme natočili opravdu údernickou formou. Filemon šlohnul na fotbalovém zápase Plzeň - Barcelona ruchový mikrofon, ale ten jsme nestačili zapojit, jaký byl fofr tenhle díl natočit. Teď trochu vážně, podařilo se nám vyzpovídat Teherána, což je náš šéf Operations v GoodData, a člověk který pracuje s Amazon AWS každý den.

pátek 28. října 2011

CZPodcast 55 - NoSQL databáze

Tímto dílem o NoSQL databázích jsem si udelěl radost, protože se o ně zajímam docela dlouhou dobu. Podařilo se nám ulovit celkem zajímavé hosty. Karmi, Augi i Honza jsou lidé kteří dnes a deně pracují s různými NoSQL databázemi (Riak, CouchDB, MongoDB, Redis, Cassandra) a mají ten dar, že o tom dokáží zasvěceně povídat. Pokud jste o NoSQL databázích neslyšeli a nebo se chcete dozvědět něco nového, pak je tenhle podcast přesně pro vás.

sobota 15. října 2011

CZ Podcast 54 - Práce v zahraničí exkluzivně z WebExpo

Natáčení tohoto dílu byla celkem velká taškařice. Jediné jasné bylo, že ho "spácháme" na konferenci WebExpo. V plánovaném čase natáčení proti nám běžely dvě jiné přednášky a nebylo moc jasné kolik lidí nakonec dorazí. Mimochodem byl to vůbec první podcast před publikem. Nakonec dorazilo kolem 30 lidí, což nás docela potěšilo, vzhledem k tomu, že proti nám šla těžká váha v podobě Johna Vanhary. Můžete si prohlédnout fotografie z natáčení.

pondělí 10. října 2011

MongoDB za scénou - jak jsme jej použili v GoodData

Jiří Tobolka napsal úvod do notifikačního API, které máme v GoodData platformě. Rád bych vás seznámil s technickými detaily a to především persistentním úložištěm, které jsme pro tento účel použili. V kostce řečeno notifikační API slouží k popisu událostí (změna určité metriky v projektu např. pokles prodeje, nárůst nových objednávek apod.), které nás zajímají a odpovídajících reakcí, které chceme vyvolat (poslání SMS, emailu, zpráva na Twitter apod.), pokud k nim dojde.

V doménovém modelu notifikací máme tři základní entity - ChannelConfiguration (reprezentuje vlastní kanál), Subscription (událost a její popis) a Trigger (způsob vyhodnocení - např. časový spouštěč, napumpování dat). Jejich vztah je zachycen na následujícím obrázku.

Před tím než jsme se pustili do vlastního kódování jsme řešili klasický problém kam s daty. V GoodData máme valnou většinu dat uloženou ve staré dobré relační databázi (RDBMS) se všemi jejími omezeními. Přibližně před rokem jsme dělali PoC, které mělo ukázat, jak složité by bylo držet tato data mimo RDBMS. Neboť charakteristika tohoto typu dat nenahrává relačnímu rozkladu. Zkusím ten problém vysvětlit právě na Notifikacích.

Jak už jsem zmiňoval doménový model se skládá ze tří entit. V případě relační databáze by výsledné relační schéma vypadalo přibližně následujícím způsobem.

Jak si můžete všimnout je zde sedm tabulek pokud počítáme i ty, které slouží k vyjádřenímany-to-many relací. Toto relační schéma přináší několik problémů. S každým novým kanálem (email, Twitter atd.) bychom museli přidat novou tabulku. Kdybychom chtěli získat konkrétní událost (reprezentovaná jako Subscription) s kanály, na které má být provedené její odeslání, museli bychom použít left outer join. Rovněž mazání by muselo probíhat kaskádově díky referenční integritě. Samozřejmě někdo by mohl namítnout, že schéma by šlo modifikovat tím nebo jiným způsobem, ale jeho komplexnost by zůstala přibližně stejná. Další možností by bylo ukládat jednotlivé entity jako dokumenty v BLOBu, ale pak bychom přišli o ten poslední výhodu, kterou by nám RDBMS svět nabídl, a to psaní dotazů např. dej mi všechny notifikace pro konkrétní projekt nebo uživatele.

Mentální kotrmelec, který je potřeba udělat, spočívá v uvědomění si, že ve skutečnosti pracujeme s dokumenty. Čteme, modifikujeme, ukládáme a mažeme dokumenty jako celky. Pokud se tedy bavíme o persistentním úložišti hledáme ve skutečnosti dokumentově orientovanou databázi. Schválně nepoužívám termín NoSQL, protože ne každá databáze z této rodiny, musí být nutně dokumentově orientovaná. Jsou zde řešení jako Riak, Cassandra, Redis a další, ale ty jsou orientované jako key/value úložiště. Při použití úložiště tohoto charakteru bychom museli naše dokumenty rozkládat v podstatě na key/value páry a nezískali bychom v tomto případě výhodu oproti konvenčním RDBMS řešením.

Přestože se bavíme o objektově orientované databázi, některé koncepty jsou velice podobné světu RDBMS. Dokumenty jsou organizované do kolekcí. Kolekce je obdobou tabulky v RDBMS. Kolekce vám umožňuje shromažďovat dokumenty s různou strukturou a nebo dokonce různého typu oproti tabulce, kde je typ ukládaných dat určen sloupci a jejich typy. Dokument se skládá z atributů (fields), které odpovídají sloupečkům, s tím rozdílem že opět neplatí omezení na určitý typ, unikátnost atd. Z této charakteristiky vyplývá, že zde nejsou žádné omezení týkající se struktury a organizace dat. Díky tomu tento typ databází nazýváme schema-less.

To sebou samozřejmě sebou přináší výhody a nevýhody nebo lépe řečeno věci, se kterými musíte počítat. Mezi výhody určitě patří jednoduché rozšiřování dokumentů o nové atributy, protože nemusíte provádět off-line migraci dat. Naopak můžete dokumenty, pokud je to potřeba, migrovat za běhu a tím snížit odstávku serveru. A to se sakra počítá, když někomu poskytujete službu se SLA a garantovanou dostupností. Na druhou stranu v případě konzistence dat a její kontroly vše leží plně na bedrech aplikační logiky.

Pojďme zpět k Notifikacím a jejich uložení do dokumentově orientované databáze. Máme zde dvě kolekce, jednu pro kanály a druhou pro vlastní události resp. jejich definici. Definice události obsahuje trigger (jak často se má kontrolovat), podmínku která se bude vyhodnocovat, zprávu která se pošle na definované kanály, a meta informace vztahující se k dokumentu. Relace na konkrétní kanály, které se mají použít je vyjádřena atributem, ve kterém je identifikátor daného kanálu. Atribut nemá žádný speciální typ, není zde ani žádné omezení na existenci dokumentu. Proto je tato kontrola na aplikační logice.

Pokud se bavíme o dokumentově orientované databází jsou zde dvě prověřená řešení MongoDB a CouchDB. Klíčový rozdíl mezi MongoDB a CouchDB spočívá v přístupu k datům a psaní dotazů. CouchDB používá k dotazování Map/Reduce funkce. Vlastní dotazy jsou speciální dokumenty nazývané view, které obsahují definici Map/Reduce funkcí. V případě CouchDB nejste schopni psát dotazy pokud nedefinujete view. MongoDB se dotazy vyjadřují pomocí strukturovaného JSON objektu, který je více deklarativní oproti programovému Map/Reduce v CouchDB. Pro zvýšení výkonnosti můžete nad atributy definovat indexy, podobně jako v RDBMS. MongoDB umožňuje psát ad-hoc query, tedy dotazy, které mohou dynamicky vzniknout například na základě uživatelského vstupu. MongoDB rovněž podporuje Map/Reduce, ale ta je určená k dávkovému zpracování dat, ne pro vlastní dotazování.

K dokumentům v CouchDB přistupujete přes RESTové rozhraní a HTTP protokol. Stačí vám tedy obyčejný HTTP klient. MongoDB používá síťový binární protokol, to vyžaduje speciální driver pro programovací jazyk, ze kterého budete k datům přistupovat.

Po zvážení všech pro a proti jsme se nakonec rozhodli pro MongoDB a to z několika důvodů:

  • možnosti dotazování - psát dotaz v MongoDB je chápání běžného smrtelníka mnohem bližší a v jistých ohledech velice podobné SQL oproti Map/Reduce funkcím. Nicméně i použití Map/Reduce je stále možné.
  • driver - velice příjemné na použití pro klienta a hlavně výkonné
  • škálovatelnost - MongoDB shardování dat bez nějakého zjevného hacku. To je velice důležité pro SAAS firmu jako jsou GoodData
  • nástroje - backup/restore/monitoring nástroje, administrační konzole, JSON and JavaScript se zde používají pro ovládání těchto nástrojů

MongoDB jsme úspěšně nasadili jak na produkční i na vývojářské instance a neměli jsme zatím žádný závažný problém. MongoDB používáme se zapnutým žurnálem z důvodu konsistence dat ve vlastním enginu přestože to zpomaluje všechny operací typu zápis. Zatím to pro nás není problém vzhledem k tomu, že většina operací je čtecího charakteru. V případě, že se tento poměr změní, můžeme nasadit MongoDB v cluster módu s replikací dat. Pak případné poškození dat na jednom z uzlů clusteru sice vyžaduje manuální zásah našich Ops, ale nemá to vliv na dostupnost služby.

úterý 27. září 2011

O frameworku na výrobu kompotů, mojí mámě a rozhodování

Představte si tu situaci, potřebujete najít framework na dělání kompotů. Máte jenom přibližnou představu jaké kompoty budete dělat. Řekněme, že máte ovoce, které tam budete sypat, potom máte sklenice a vodu. Když budete moje máma, budete váš myšlenkový pochod vypadat následovně. No jablka a hrušky ze zahrádky okrájím, hodím to dovnitř, zaleju vodou a nastavím ať se to vaří půl hodiny. Pokud budete založením systémový analyzátor problémů, bude váš myšlenkový pochod asi následující. Zanalyzujete typ stromů a ovoce, které vám roste na zahradě. Spočítáte si průměrnou úrodu a spotřebu za poslední deset let, včetně kontroly skladových zásob. Z toho začnete uvozovat ideální poměr velikosti sklenice, velikosti ovoce, doby vaření a teploty. Se zvážením všech kritérií budete pociťovat nepříjemné šimrání nejistoty kdesi vzadu a proto přistoupíte k otestování. Navaříte dvě sklenice v každém frameworku a pro jeden z nich se rozhodnete. Samozřejmě pořád s tím samým blbým pocitem nejistoty, protože dvě sklenice je přece málo a vy potřebujete mnohem víc časů, víc testovacích vzorků, víc...

Rozdíl mezi přístupem mojí mámy a systémového analyzátora (horší název typu člověka už mě napadnout nemohl) bude v tom, že zatímco u nás doma si budeme dopřávat zeleninu a ovoce z komponentu celkem pravidelně a brzo, tak vám dojde sranda až budete muset zavařit znojemské okurky, protože s těmi jste na začátku vůbec nepočítali. Protože okurky prostě nerostou na stromech a nebylo to v zadání.

Poučení z výše uvedeného příběhu je v tom, že když budete dlouho otálet s výběrem jakéhokoliv frameworku stanou se vám dvě věci. Za prvé se nebudete soustředit na to co vás živí, a za druhé nikdy neodhadnete co budete v budoucnu opravdu potřebovat. Zapomeňte na cokoliv,co trvá vybrat déle než týden. Vemte to co vám do rukou padne na první pokus a postavte na tom izolovanou část systému. Pokud se to osvědčí iterativně přidávejte. Nebojte se udělat krok zpátky. Neosvědčilo se? Ok, tady je poučení, které z toho máme. Obvykle mívám pocit silného "systémového přístupu" při výběru webového frameworku. Bohužel většinou to končí naprosto tragikomicky, protože se analýzou dojde k tomu, že nejlepší bude, když si to napíšeme sami. To je pochopitelně to nejhorší co můžete udělat z dobře známých důvodů (udržovatelnost, dokumentace, křivka učení atp.).

Nedávno jsme s Lukášem Křečanem řešili klasický problém "kam s ním". Šlo o data, které byla z povahy dokumentově orientované. Nakonec jsme vybrali MongoDB, přestože jsme před tím nezkoušeli minimálně pět dalších typů databáze co nám kdo podsouval a ani netrávili týdny zkoumáním co by se nám kde mohlo hodit. V podstatě jsme sáhli po tom prvním co nám padlo do ruky a ono to funguje.

Svět je prostě plný nejistot a neznámých a pokud v něm chcete dělat rozhodnutí nemůžete se spoléhat, že je všechny rozklíčujete. Zjednodušeně řečeno: ten kdo se dokáže rychle adaptovat se na měnící podmínky vítězí. Řiďte se tím nejenom pokud vybíráte zavařovač ovoce a zeleniny, ale i výběr databáze nebo webového frameworku.

úterý 20. září 2011

CZPodcast 53 - rozhovor s René Steinem

Do dalšího dílu podcastu se podařilo dostat dlouho slibovaného a neméně dlouho odkládaného Reného Steina. René je kovaný .NETář a freelancer, který se zabývá školeními Návrhových vzorů, UML a programovaní vůbec. My jsme se kromě těchto témát dotkli například agilních metodik, Windows Phone 7 a budoucnosti Microsoftu, která podle Reného nemusí být vůbec tak špatná, jak se může zdát. Pokud se chcete zůčastnit živého natáčení dalšího dílu budete mít příležitost na konferenci WebExpo 24.9.2011 od 17:30-17:45.

středa 31. srpna 2011

CZPodcast 52 - Service virtualization

Service virtualization je jedna z věcí, která nas s Filemonem zajíma a proto jsme využili starých kontaktů z dob Systinetu a vyzpovídali Honzu Odstrčila z HP, který pracuje na produktu HP Service Virtualization.

pondělí 22. srpna 2011

Technologický dluh vs. Overengineering

Technologický dluh je metafora popisující situaci, kdy vědomě či nevědomě uděláté technické rozhodnutí, které funguje v krátkodobém horizontu, ale z hlediska dlouhodobého vám může způsobit problémy. Příklad: máte kód pro přístup k databázi máte rozlezlý po celé aplikaci namísto, aby byl v jedné vrstvě.

Overengineering je případ kdy pro určití problém zvolíme příliš složité řešení. Příklad: potřebujete na HTTP vystavit XML dokument a nenapadne vás nic lepšího než použít Web Services se WS security dohromady.

Nikdy jsem si to neuvědomil, ale při vývoji jsou to právě tyto dvě kritéria nebo hranice, mezi kterými balancujeme. Zajímavé je, že příčinou tlaku jednoho nebo druhého jsou různé faktory. Product management nás tlačí do toho, abychom doručovali co nejrychleji nové vlastnosti a tedy nepřímo žili na technologický dluh (ten rozumný ví, že to jde jenom z části). Naopak naše inženýrská hrdost nás tlačí ke křišťálově čistým a někdy příliš komplikovaným řešením.

Vybalancování mezi technologickým dluhem a Overengineeringem je něco jako chůze na visutém laně. Jednoduše muže zavrávorat a spadnout na hubu. Nejde jednoduše říci jestli je lepší mít technologický dluh a nebo komplikovaná a překombinivaná řešení.

Důležitým faktorem je v jaké oblasti si technologický dluh a overengineering sekneme. Může to být design, vývoj, kód a deployment. Obecně jsme asi schopni akceptovat technologický dluh v kódu, protože s dodržením zapouzdření dokážeme izolovat problematickou část do jednoho místa a nenechat jej rozlézt jinam. Mnohem horší je to už na úrovni designu, kde se každá chyba násobí každou fází, kterou se projde až k samotnému nasazení.

Nenapadá mě žádná poučka snad kromě: dělejte věci tak jednoduše jak to jde, ale ne jednodušeji.

K tomuhle opravdu krátkému zamyšlení dodám tři odkazy na téma technologický dluh.

pondělí 8. srpna 2011

Aplikace kontra Služba kontra Knihovna

Jeden z problémů, ke kterému dojde při kontinuálním vývoji aplikací je její neustále nafukování přihazováním nové funkcionality někdy společně s novými technologiemi, které k tomu potřebujete. Klasickým příkladem je webová aplikace její WAR, který se nafukuje jako otesánek. Přijde mi, že skoro nikde se vlastně nemluví a neakcentuje o tom jak tenhle problém řešit a každý se musí učit z vlastních chyb. Já se v tomhle článku pokusím nejdřív popsat v čem je problém otesánka a jak si představuji jeho řešení. Budu to popisovat z pohledu, který vnímám při vývoji u nás v GoodData, kdy jsme Software As A Service poskytovatel.

Koncept otesánek

Alias: monstrum

Popis: aplikační logika má lokální interface (z pohledu JVM). Všechny použité knihovny musí být na classpath a vhodně nakonfigurované a zinicializované při startu aplikace. Volání jednotlivých aplikačních celkú je vždy synchronní. Znovupoužitelnost kódu je na úrovni knihoven.

Třída problémů

Deployment

Rychlost inicializace je přímo úměrná velikosti, jedná se především o konfiguraci aplikační logiky a knihoven, které se používají. Rychlost inicializace může mít negativní dopad na down time aplikace (doba nutná k odstávce), pokud nemůžeme běžet paralelně starou a novou verzi aplikace. Velikost deployvatelného balíčku (desítky megabajtů) znesnadňuje rychlost distribuce pokud chceme například udělat simultánní deploy v clusteru.

Jednotný classloader prostor diktuje použití stejných verzí knihoven, to se týká především knihoven třetích stran. Nutnost použití novější verze knihovny může vést k tomu, že musíme upravit a otestovat části aplikace, kterých by se změna nemusela vůbec dotknout.

Runtime

Nezřízené nároky na paměť, nemůžeme rozdělit paměť přidělenou jednotlivým částem aplikace a s tím související vzájemné interference mezi nimi. Vyšší spotřeba jedné části aplikace může mít negativní dopad na zbytek systému. To se týká prakticky jakýchkoliv sdílených prostředků na úrovní JVM.

Synchronní volání jednotlivých aplikačních bloků mezi sebou blokuje volajícího a neumožňuje efektivně reagovat na výpadek části systému, který není z pohledu vyřízení požadavku kritický (zápis do audit logu, notifikace).

Údržba

Příliš mnoho funkcionality neumožňuje jednoduchou odstávku, oprava chyby nebo servisní restart, protože má dopad na systém jako celek. Složitější monitoring, v podstatě blackbox s tunou odpovědností.

Řešení

Pokud je vztah částmi aplikační logiky takový, že na sobě nejsou přímo závislé (např. transakční kontext) a jejich vzdálená komunikace není výkonnostním problémem, pak je řešením jejich zapouzdření jako služeb. Služby nám umožňují vytvořit systém, který bude volněji provázaný. Díky tomu nám odpadne řada problému, které vznikají těsnými vazbami. To se týká všech oblastí (deployment, runtime, údržba), které byly výše popsané. Jedna z příjemných věcí, kterou tím získáme je koncept okleštěných služeb. Ten umožňuje v případě problému odstavovat služby, které nejsou pro běh systému kritické a zachovat primární ůčel systému. Pokud máte herní systém pro online hry, pak jeho primárním učelem je, aby mohli hráči pařit a pokud vám začne kolabovat sekundární systém např. registrace nových hráčů, pak není důvod, aby to vzalo sebou i ty stávající.

Služby nám umožňují, aby byly používány nejenom z jazyka, ve kterém byly samy napsány, ale i v jazycích jiných. Jejich použití je prakticky omezené jenom jejich rozhraním. To nabízí daleko lepší znovypoužitelnost. Každá služba může definovat vlastní podmínky použití (SLA) jako jsou doby odezvy, maximální počet obsloužených požadavků apod.

Samozřejmě to, že část aplikační logiky běží jako služba vystavená například přes REST a nebo messaging přináší jinou třídu problémů. Návrh rozhraní služeb a jeho vývoj je věc poměrně drahá pokud se bavíme například o RESTu. Vzdálená komunikace přes síť sebou přináší problémy se spolehlivostí resp. jediné ne co se můžete spolehnout je, že nic není spolehlivé. Tomu musí odpovídat i design a implementace konkrétní služby.

Tohle určitě není renesance zprofanovaného slova SOA, to slovo se mi příčí a je kolem něj tolik balastu, že je těžké si pod ním něco konkrétního představit. Tohle je lekce, kterou jsem se naučil postupem času.

středa 27. července 2011

Proč je v Jave obtížné psat thread safe

 
final Map m = new HashMap();

ExecutorService executorService = Executors.newFixedThreadPool(1);

Callable<Object> callable = new Callable<Object>() {
 public Object call() throws Exception {
      m.put("test", "test") 
      return null;         
 }
};

Future<Object> future = executorService.submit(callable);
future.get();

assertTrue(m.contains("test"));

Protože ani po deseti letech intenzivního používání Javy si nejsem absolutně jistý jestli je tenhle kód thread safe a nebo není. Thread safe to IMHO není, protože ta mapa není zapisovaná a čtená přes stejný zámek. Na druhou stranu, pokud by ta blokujici fronta uvnitř ExecutorService a zápis do Future objektu způsobil flush (synchronized nebo lock tam bude) lokální cache obouch těch vláken, tak by to fungovat mohlo. Ale spoléhejte na to. Osobně bych tu mapu zasynchronizoval a nebo použil ConcurrentHashMap. Je to thread-safe díky sémantice happen-before, která je tranzitivní a podle javadocu java.util.concurrent zaručena pro vložení tasku a získání jeho výsledk./p>

  • Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins. Similarly for Callables submitted to an ExecutorService.
  • Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.get() in another thread.

neděle 17. července 2011

Pětiminutové intro do Mockito knihovny

Pár lidí se mě ptalo na Mockito a po té co ji vyzkoušeli mi dali za pravdu, že se jedná opravdu o zásadní nástroj pro psaní testů. Není mým cílem popsat všechny vlastnosti této knihovny, ukážu pouze to nejzásadnější s pár drobnými komentáři. I tak čtenář neznalý této knihovny dostane do rukou návod, který mu vystačí na 80% případů, se kterými se střetne při testování.

Jedním z důvodu proč mají vývojáři odpor pro psaní testů je omáčka, kterou je potřeba vytvořit pro nastavení vlastního prostředí testovaného objektu a složitost verifikace výsledků samotného testu. To je přesně situace, ve které Mockito tahá kaštany z ohně.

Mockito slouží k mockování tříd a jejich chování a kontrolu jejich interakce s okolním světem. Neznám český překlad slova mock a mockování a dosti silně pochybuji, že vůbec existují. Mock je dynamicky vytvořený objekt s rozhraním dané třídy. Mockování je definice chování mocku při interakci s okolním světem, příklad volám na mocku metodu X, chci aby se vrátila hodnota 1.

Poznámka na okraj. Mockito mi pomohlo v pochopení, že kód který není dobře objektově navržený není možné efektivně otestovat. Kromě všech vlastností, kterými mi pomáhá při psaní testů, zároveň nastavuje zrcadlo objektovému návrhu a nutí mě přemýšlet jakým způsobem jej vylepšit, což zpětně vede k lepší testovatelnosti.

K jednoduché ukázce Mockita jsem si vybral dva objekty Basket a Item představující nákupní košík respektive zboží v něm uložené. Nákupní košík má metody pro přidání nového zboží, vyčištění košíku a vrácení celkové ceny vloženého zboží. Zboží má pro jednoduchost pouze metodu, která vrací jeho hodnotu.

    public class Item {

        private final double price;

        public Item(double price) {
            super();
            this.price = price;
        }

        public double getPrice() {
            return price;
        }
    }
    public class Basket {

        private final List<Item> items;

        public Basket() {
            this(new ArrayList<Item>());
        }

        Basket(List<Item> items) {
            super();
            this.items = items;
        }

        public Basket addItem(Item item) {
            this.items.add(item);
            return this;
        }

        public List<Item> getItems() {
            return items;
        }

        public void clear() {
            this.items.clear();
        }

        public double getTotalPrice() {
            double totalPrice = 0;
            for (Item item : items) {
                totalPrice += item.getPrice();
            }
            return totalPrice;
        }
    } 

Klíčovou třídou pro použití Mockita představuje org.mockito.Mockito. Ta má spoustu statických metod a vyplatí se proto udělat její statický import. Nejzásadnější metody jsou:

  • Mockito.mock - jako argument dostává třídu, ze které vytváří mock (třída nesmí být final).
  • Mockito.when - umožňuje definovat chování (vrať hodnotu, vyhoď vyjímku) při volání dané metody třídy
  • Mockito.verify - umožňuje zkontrolovat, jestli daná metoda mocku byla volaná a to včetně argumentů, se kterými byla volaná
  • Na prvním příkladu si ukážeme jak otestovat očekávané chování metody pro celkovou cenu zboží uložené v košíku. Připravíme si dvě mock instance zboží, které vložíme do košíku.

        public void testGetTotalPrice() {
            Basket basket = new Basket();
            Item item1 = mock(Item.class);
            when(item1.getPrice()).thenReturn(10d);
            Item item2 = mock(Item.class);
            when(item2.getPrice()).thenReturn(20d);
    
            basket
                .addItem(item1)
                .addItem(item2);
    
            assertThat("The total price must be sum of all prices in basket", basket.getTotalPrice(), is(30d));
        }
    

    Při mockování zboží, představovaného třídou Item jsme nejdříve vytvořili mock a po té jsme určili chování metody getPrice. V tomto případě by bylo možné namítnout, že není důvod třídu Item mockovat a použít přímo její instanci. Osobně se snažím všude používat mocky z toho důvodu, že testovací třídy a metody jsou na sobě méně závislé.

    Dalším testem chceme ověřit, že košík se při volání metody clear opravdu vyčistí. K tomuto účelu můžeme použít právě techniku ověření interakce s okolním světem. Já jsem zamockoval interní list, ve kterém si košík drží všechny položky a na něm ověřil volání jeho metody pro vyčištění.

        @Test
        public void testClear() {
            List<Item> items = mock(List.class);
            Basket basket =  new Basket(items);
            basket.clear();
            verify(items).clear();
        }
    

    Poslední řádek právě verifikuje volání metody clear na mocku. Podobným způsobem se zamockováním interního listu lze otestovat i přidávání zboží do košíku. O tom, že je tato technika trochu kontroverzní se zmíním vzápětí.

        @Test
        public void testAddItem() {
            Item item = mock(Item.class);
            List<Item> items = mock(List.class);
            Basket basket =  new Basket(items);
            basket.addItem(item);
            verify(items).add(item));
        }
    

    Při používání mocků a verifikace jejich interakce s testovaným objektem má několik nevýhod. V případě, že začnete tuto verifikaci provádět, tak si ověřujete interní implementaci, která by měla být testu absolutně ukradená, pokud se bavíme o black box přístupu. Je na zvážení, co je potřeba ještě verifikovat, protože změna implementace může znamenat, že testy přestanou procházet jenom z toho důvodu, že byly vázané na implementační detaily. Další nevýhodou je fakt, že někdy musíte pro testy porušit zapouzdření a vystavit právě onu interní část, kterou je potřeba zamockovat.

    Mockito toho umí daleko více než metody zmíněné v tomto článku. Můžete například verifikovat i argumenty se kterými se volá mock, můžete otestovat, že nedochází k interakci. Můžete dělat částěčně mocky. Každopádně základ práce s Mockitem pokrývají právě tyto tři metody.

    neděle 12. června 2011

    Dependency Injection je cesta, nikoliv cíl

    Když jsem asi před šesti lety objevil dependency injection (dále v textu DI) jako způsob pro odstranění těsných vazeb mezi objekty, připadalo mi to jako Svatý grál programování. Díky masivnímu rozšíření Spring frameworku a zanesení DI do všech dalších frameworků a standardů, které přišly po něm se DI stalo běžnou součástí dnešního programování. Bohužel DI používáme aniž bychom o tom přemýšleli a na místech kde to není třeba.

    V předchozím odstavci jsem DI popsal jako způsob, ano DI je způsob a nebo realizace chcete-li něčeho čemu se říká Inversion of Control, dalším realizací je Service Loator. Klíčové je to, že nechceme naše objekty zatěžovat tím, aby musely vědět o dalších objektech víc než je potřeba, protože tím si na sobě vytvářejí těsnou vazbu. Pokud mají mezi sebou objekty naopak vazbu volnou, umožňuje nám to jejich jednoduší testovatelnost a další výhody.

    Posíleni touto mantrou jsme začali vytvářet komplexní grafy popisující vztahy mezi našimi objekty v XML. Asi na sto řádků kódu jsme získali dvacet řádku XML deskriptoru, a teď pozor, aniž by nám to přineslo něco kromě bolehlavu při refaktoru názvů tříd a packagu a dohledávání toho proč to sakra nefunguje s výsledkem, že nějaký šikula změnil název beany.

    Vkrádá se mi na jazyk pochopitelná otázka, kdy tedy DI použít a kdy mít prostě těsnou vazbu mezi objekty. Já mám dvě pravidla. Pravidlo číslo jedna, pokud se jedná o infrastrukturu (messaging, transakce, blablabla) a nebo věci spojené s deploymentem vždycky to řešte přes DI. Není nic horšího než si zašpinit business logiku infrastrukturním kódem. Druhé pravidlo zní, DI umožněte, ale zároveň nechte objekt poradit si těsnou vazbou.

    public class SweetService {
        private final LittleDao littleDao;
        
        public SweetService() {
            this(new LittleDao());
        }
        
        public SweetService(LittleDao littleDao) {
            this.littleDao = littleDao;
        }
    }
    

    Výše uvedený kód přesně tenhle přístup ilustruje. Úmyslně používám zprofanované termíny Service a DAO, o jejichž významu předpokládám, že je čtenáři zřejmý a nemusím tak kód nijak vice komentovat. Třída SweetService umí vytvořit instanci LittleDao bez pomoci zvenčí, a zároveň jí umí přijmout jako argument konstruktoru. Pokud bude někdy potřeba nějaká specializace třídy LittleDao mám pořád možnost použít konstruktor, kde je argumentem. Mimochodem používám to vždy když třídu testuji pomocí Mockita a vkládám si tam mocky, na kterých verifikuji její chování.

    V 99% případu si vaše obyčejné objekty vystačí s implicitním konstruktorem, který bude obsahovat těsné vazby. Pokud nastane situace, že se konstrukce zesložiťuje, mělo by přijít zamyšlení jestli náhodou nevytváříte třídu s úlohou alfa samce. V takovém případě je dobré se podívat na kód s trochu větším odstupem a zkusit ho rozbít na menší objekty s jasnou odpovědností.

    Nyní je na čase ptát se co tím získáme. Nezávislost kódu na okolí. Objekt dokáže pracovat i bez toho aniž by ho bylo nutné někde registrovat. Menší množství integračních testů (přímá úměra s tím jak rychlé ty testy jsou a jak rychle zjistíte, že se něco někde rozbilo), kterými bychom museli pokrýt externí konfiguraci. Rychlost startu aplikace související s velikostí, které je potřeba zpracovat uvnitř IoC kontejneru. Přehlednost aplikace a kódu, všechno co je mimo kód se špatně dohledává a spravuje.

    Když mluvím o konfiguraci je tu samozřejmě možnost používat anotace a tím odstranit, některou z nevýhod externí konfigurace a XML. Anotace jsou dobrou volbou, ale mají svoje nevýhody - těsné navázání na konkrétní typ, nemožnost určit defaultní implementaci (platí pro Spring framework) a dříve nebo později nutnost mixování s XML, protože ne všechno lze pomocí nich vyjádřit.

    Pokud DI není cíl, ale cesta, pak cílem by měl být správný objektový návrh. DI k němu dokáže velmi dobře pomoci jenom se musí nad jeho konkrétní použitím přemýšlet v širším kontextu.

    úterý 17. května 2011

    Visitor prakticky

    Jedním z mých oblíbených návrhových vzorů je Visitor, bohužel se nesetkávám v moc často s jeho použitím. Jako u většinou návrhových vzorů snad kromě totálně provařeného Singletonu to vypadá tak, že teoreticky všichni známe všechny vzory a prakticky umíme použít jenom ten jeden nebo dva. Tenhle článek je moje praktické připomenutí tohoto k čemu tento vzor používám.

    Visitor používám pro případy, kdy mám košatou objektovou strukturu se kterou potřebuji pracovat. Typicky se jedná o operace: projdi strukturu a najdi všechny objekty vyhovující danému kritérie nebo projdi strukturu a někde něco pozměň.

    Mějme objekty Tree, Branch a Leaf, které se agregují. To znamená, že Tree má N Branch a ta zase N Leaf.

    Klíčové je uvědomit si, jak bude vypadat kód jakékoliv operace, kterou budeme chtít nad touto strukturou udělat. Ve všech případech tam bude jeden a ten samý kód, který tuto strukturu bude prolézat a potom tam bude malé množství kódu, které provede vlastní operaci s danou strukturou.

    To je přímo přesné zadání pro aplikaci návrhového vzoru Visitor. Společný kód, tedy hierarchické prolézání struktury do hloubky, si bude implementovat každý objekt v dané agregaci sám. Operace nad každým jednotlivým objektem bude delegována na Visitor, který bude předán jako argument metody.

    To úžasné na tomto vzoru je, že jakékoliv operace nad strukturou mohou být přidávány aniž bychom zasahovali do původních objektů. Zároveň mohou být být čistě zapouzdřeny do objektů. Odpadá nám špagetový kód (procedurální) kód, ve kterém bychom míchali prolézání struktury s danou operací nad ní. Máme čisté oddělení odpovědností. Díky tomu se nám zjednoduší testování, což je skoro vždy důkaz dobrého objektového návrhu.

    Řekněme, že chceme napsat operace, která nám každý druhý lístek na stromě obarví na žlutou barvu.

        public class Marker implements Visitor{
            private int leafCounter = 1;
    
            @Override
            public void visitLeaf(final Leaf leaf) {
                if(leafCounter % 2 == 0) {
                    leaf.setColor("yellow");
                }
                leafCounter++;
            }
        }
    

    Test je pak velice jednoduchý (využívám knihovnu Mockito)

        import static org.mockito.Matchers.anyString;
        import static org.mockito.Matchers.eq;
        import static org.mockito.Mockito.mock;
        import static org.mockito.Mockito.never;
        import static org.mockito.Mockito.verify;
    
        import org.junit.Test;
        public class MarkerTest {
    
            @Test
            public void testVisitLeaf() {
                Leaf leaf = mock(Leaf.class);
                Marker marker = new Marker();
                marker.visitLeaf(leaf);
                verify(leaf, never()).setColor(anyString());
                marker.visitLeaf(leaf);
                verify(leaf).setColor(eq("yellow"));
            }
    
        }
    

    Použití konkrétního visitoru pak vypadá následovně:

        Tree tree = new Tree();
        Marker marker = new Marker();
        tree.accept(marker);
    

    Zdrojové soubory jsou k dispozici na GitHub.

    neděle 1. května 2011

    Stručný průvodce code review

    Nejsem žádný velký příznivce code review, k tomu už jsem se kdysi dávno přiznal, na druhou stranu neznám jiný proces pro zachycení těch nejpalčivějších problémů, které by na vás vypadly dříve nebo později. Za těch pár let co jej dělám jsem si vypracoval seznam oblastí, na který se vždycky vyplatí podívat a pár pravidel, které se snažím dodržovat.

    Code smells a OOP design

    Tohle je obecné pravidlo, které vychází z toho, že kód, který je správně objektově navržen není tak náchylný k problémům. Speciální pozornost věnuji třídám, které vystupují v roli Centrálního Mozku Lidstva, bezpečně je poznáte podle toho, že mají hromadu procedurálního kódu. Návrhový vzor Business facade nebo Service facade neznamená, že do jedné třídy napěchujeme všechnu logiku, jak si bohužel mnoho programátoru myslí.

    Když už mluvíme o smraďoších v kódu, tak se mnohdy stačí podívat na třídy, které mají největší velikost případně největší testy a nebo nemají testy vůbec. Povětšinou platí pravidlo, že kód, který se těžko testuje nebývá obvykle dobře navržený a jsou v něm problémy.

    Osobně při code review koukám také na to jestli je kód dostatečně defenzivní. Co všechno musí být ještě public, proč něco není final. Všechno co smrdí duplicitou musí okamžitě pryč. Stejně tak jakýkoliv zakomentovaný kód, pokud je potřeba nějaká ukázka šup s ní do javadocu. Když tu mluvim o kódu mám na mysli i všechnu infrastrukturu okolo tj. Maven, Spring atd.

    Synchronizace a bezpečnost z hlediska vláken

    Nejproblematičtější částí co do možných problémů představuje kód, který je špatně proveden z hlediska paměťové konzistence při vícevláknovém přístupu. Pokud dneska programujete jakoukoliv webovou aplikaci případně obsluhu messagingu tak jste tomuto problému vystaveni aniž byste si to možná uvědomovali.

    Programátoři si dobře uvědomují problém vzájemného vyloučení vláken pomocí zámků, ale problémem je uvědomit si, že synchronizační primitiva zároveň zaručují i viditelnost dat mezi vlákny. To je problém pokud si objekty předáváte mezi různými kontexty, které jsou obsluhovány různými vlákny. Na tohle konto zbývá dodat: a bude hůř například díky Servlet 3.0 a jeho podpoře asynchronicity.

    Zaměřte se proto na objekty, které drží nějaký stav. Důležité je také zkontrolovat chování cizích knihoven, které používáte. Jestli byly navržené na vícevláknové použití, které odpovídá vašemu setupu. Obecně řečeno, nejméně problémů si způsobíte pokud budete programovat bezstavové služby s tím, že stav budete předávat v imutabilních objektech.

    Zámky/Deadlocky způsobéné voláním sebe sama a jiné špinavosti

    Hodně problémů bývá v různých typech zámků ať již paměťových a nebo v databází. První věcí je ověření, že zámek vůbec může fungovat. Hodně často je zámek dobře použitelný v lokálním provedení, v jedné běžící instanci JVM. Pokud máte cluster deployment je nutné si ověřit, že zámek bude fungovat i v clusteru.

    Pokud už potřebujete zámek, který ma lídově řečeno fakčit v clusteru, tak bude pravděpodobně navržen nad databází. Zde je potřeba pečlivě projít uvolňování zámku, nejlépe na příkladu spadlé instance JVM. Mimochodem když už mluvíme o zámcích podobnou sortu problémů představuje obecně kód, který nějakým způsobem závisí na pořadí inicializace. Opět věc, která může dělat problémy v clusteru.

    Samostatnou kapitolou je využívání zdrojů, které jsou poskytované z poolu například databázová připojení, HTTP vlákna atd. Zde je potřeba zkontrolovat, že v rámci jednoho volání nedochází k alokaci více než jednoho zdroje. Pokud ano, dříve nebo později vás s velkou pravděpodobností čeká deadlock. K tomu stačí, aby byl počet vláken přistupujících k poolu stejný nebo vyšší jako počet zdrojů v poolu.

    Uvolňování zdrojů a jejich alokace

    Pokud alokujete zdroje, které je explicitně potřeba vrátit zpět. jako je paměť, file descriptory atd. zkontrolujte, že se tak děje v try/finally blocích. Opravdu záludným problémem bývá vícenásobné přiřazení do proměnné, která ukazuje na zdroj, který je potřeba vrátit. Docela dobrým pravidlem bývá zkontrolovat, že se používají final proměnné. To tento typ problému minimalizuje.

    Při alokaci zdrojů je potřeba prověřit jakým způsobem dochází k jejích uvolnění. Pozor je potřeba dávat na vše vázané nějakým způsobem na transakce. Pokud může nastat situace, že budeme mít dlouhotrvající transakci je potřeba ověřit, jakým způsobem a jestli vůbec se po dobu jejího běhu uvolňují alokované zdroje. Vedlejším efektem transakcí totiž bývá alokace paměti či zamykání dalších zdrojů (řádky, tabulky databáze).

    Pár postřehů

    Představa, že během code review odhalíte všechny problémy, které tam někdo nasekal za N dní prace je nesmyslná. Code review by nám mělo pomoci odstranit ty největší boty, všechno ostatní je nad plán. Pokud problémy nehodláte odstranit hned a nebo na ně nevznikne nějaký defekt, ke kterému se později vrátíte, tak code review nemá smysl snad kromě sdílení vědomostí o kódu.

    Zaměřte se na další detaily jako: jak dlouho trvá exekuce testů, o kolik se prodlouží start aplikačního serveru, o kolik víc paměti kód sežere, jak dlouho se to bude buildovat. To jsou všechno otázky, které vám během code review pomohou udělat si celkový obrázek o tom co máte před sebou.

    pondělí 18. dubna 2011

    Obrácený design

    V prezentaci na téma Java v Cloudu jsem mluvil o problému, kterým si myslím trpí většina softwárových návrhů, které jsem v poslední době slyšel. Soukromě jsem ho pokřtil Obrácený design a souvisí s posedlostí se kterou se snažíme šroubovat Enterprise Java řešení a technologie na všechny typy problémů, které před námi stojí. Během prezentace jsem použil slovní pyrotechniku "...lituji každého slova na téma Spring, které jsem kdy pronesl na blogu", teď bych rád nabídl trochu obšírnější popis toho co je Obrácený design a jak do něj výše pronesená a zapsaná věta zapadá.

    Mějme aplikaci jako je LinkedIn sloužící k prezentaci a poskytování profesních profilů jednotlivých uživatelů. Ta je založena na tom, že si tam v podstatě vedete CV online a jako reference používáte kolegy, se kterými jste pracovali nebo pracujete.

    Pokud se zeptám 10 Java vývojářů, jak takovou aplikaci navrhnou naimplementují, tak odpověď 9 bude v kostce vypadat následovně. Použiju relační databázi, do ní namapauju entity pomocí Hibernate, transakce budu řídít přes Spring, bude tam DAO a servisní vrstva. Pro webovou vrstvu určitě MVC framework. Na otázku kde budou tohle řešení provozovat bezelstně odpoví, že na Google App Engine. Sice s tim nemají žádné zkušenosti, ale prý se na něm dá dobře škálovat.

    Přesně tohle je Obrácený design, jsme posedlí technologiemi a vsrtvami aniž bychom se zaměřili na to co je podstatné. Slepě aplikujeme postupy, které jsme viděli a o kterých toho nejvíc víme a zdají se nám jako svaté. Stop, stop a ještě jednou stop. Vraťmě se na záčátek. Je potřeba protřepat hlavu a podívat se na ten problém od jeho základů.

    Moje historická zkušenost mě vede k tomu, že aplikace se nenavrhují od středu, ale od spodku a hořejšku. Na začátku bychom si měli položit otázku, s jakými daty budeme pracovat a jaké operace na nich budeme chtít provádět. Budeme data spíš číst nebo zapisovat, jaký bude poměr zápisu a čtení. Máme jenom jeden typ dat a nebo máme různé kategorie. Jaký bude objem těch dat. Budu potřeba vyhledávat, bude potřeba dělat složité dotazy a nebo mi bude stačit CRUD. Jaká musí být konzistence dat, můžeme si dovolit "zvětralá" (stale) data.

    Požadavky na operaci s daty do jisté míry napovídá cosi o tom, jak bude vypadat klient. Potřebujeme tlustého nebo tenkého klienta, budeme mít jenom jeden typ klienta nebo více. Chceme mít API pro integraci s dalšími službami. Kde budeme držet stav komunikace, můžeme si to dovolit na serveru. Jak velké objemy dat si můžeme dovolit přenášet vzhledem k rychlosti.

    Pokud si odpovíme na výše uvedené otázky vypadnou nám celkem jasné požadavky nejenom na návrh střední vrstvy, ale i na konkrétní technologie, které nám budou vyhovovat. Dokonce tím získáme i odpověď na jaké infrastruktuře jsme schopni řešení hostovat. Ve skutečnosti pravděpodobně dojdete k tomu, že je úplně jedno jestli použijete Spring nebo Google Guice, protože to bude opravdu až ta poslední věc, která vás bude pálit.

    Prosím ještě jednou nedesignovat od středu :-).

    čtvrtek 14. dubna 2011

    CZ Podcast 50 - Vývoj her s Centauri production

    V jubilejním padesátém dílu jsme si s Filemonem splnili společné přání a zašli jsme do herního studia Centauri production.


    pátek 25. března 2011

    CZ Podcast 49 - Nangu TV

    Tentokrát jsem u toho nebyl, ale o to víc jsem se u poslouchání bavil díl. Člověk by netušil co se tu kutí za zajímavé projekty.

    čtvrtek 17. března 2011

    Monitorování Java aplikací v cloudu

    Jedna z lekcí, kterou vám dá cloud a hostování vlastních služeb, je v tom že přestávají platit staré přístupy, které vám léta fungovaly a nad kterými nebylo třeba nutno přemýšlet. V GoodData vycházíme z DevOps modelu to znamená, že vývojář jde se svým vývojem až do produkce. Nemáme tedy partu chlapíků, kteří sedí někde v servrovně a nemají na práci nic jiného než koukat kde se co děje. V produkčním clusteru nám běží N instancí JVM, které navíc můžeme dynamicky zapínat a vypínat podle očekávaného/známého zatížení. K tomu máme další desítky instancí, které běží vývojových a testovacích serverech a vývojářských instancích. Potřebujeme tedy monitoring, který nám umožní všechny tyto běžící JVM a aplikace v nich monitorovat.

    Vždycky jsem byl nadšený z toho, kolik nástrojů je v ekosystému Javy k dispozici, bohužel jsem si neuvědomil, že můj pohled je značně omezený na potřeby řadového vývojáře. Máme sice jednoduché profilery a monitorovací nástroje typu VisualVM a nebo plnohodnotné profilery, ale s jejich použitím se počítá právě v rámci vývoje.

    V GoodData Máme dva typy deployment prostředí z pohledu Java artefaktů/komponent, které hostujeme. Jeden typ komponenty běhá uvnitř servletového kontejneru a druhý ten stěžejní je standalone proces. Zkoumali jsme možnosti, které nabízejí nástroje NewRelic, JavaMelody a Hyperic a výsledek pro mě byl velkým zklamáním.

    Požadavky

    Z hlediska správy chci mít na jedné adrese jedno plátno (dashboard), na kterém si najdu instanci, která mě zajímá a podívám se na její detaily. Chci mít možnost přidávat si statistiky, které mě zajímají. Tedy kromě toho, že je sledují, tak musím mít možnost je nějak definovat. Příklad zajímá mě počet nějakých API požadavků, objem dat, které mi protečou skrze části systému a nebo čas zde strávený. Jinými slovy potřebuji customizovatelnost.

    Ze základních charakteristik JVM nás zajíma:

    • maximalání velikost heapu a jeho obsazeni v čase
    • využití CPU v čase
    • uvolňování paměti Garbage Collectorem a četnost jeho pouštění v čase
    • počet tříd v čase
    • stav vláken v čase

    Když vezmu ty nástroje popořadě. NewRelic i JavaMelody jsou výrazně orientované na monitoring webových aplikací a poskytují velkou část monitoringu, kterou vůbec nevyužiji. NewRelic sice umožňuje monitoring background procesů, ale jeho pricing není moc přívětivý vzhledem k objemu instancí, které tím potřebujeme monitorovat. Hyperic vypadá více genericky a umožňuje vytvářet vlastní metriky nad JMX beanami. Na druhou stranu není moc přehledný a Java v něm vypadá, že je jenom tak mimoděk.

    Závěr

    Jsem rozhodnuty, že pokud se neobjeví zázrakem nástroj, který se mým očím až doteď vyhýbal, tak si ten monitoring napíšeme prostě sami. Ne nechytejte se za hlavu, to opravdu myslím vážně. Už teď používáme Splunk, který nám umožňuje na všech instancích sbírat informace z logu, dělat nad tím dotazy a z výsledků stavět tabulkové reporty včetně grafů. Tedy tu správu, po které volám. Ve Scale/Jave si napíšeme jednoduchého agenta, který bude přisátý na JVM, bude olizovat její údaje a bude je dávat do logu ve formátu, nad kterým se nám budou dobře dolovat ve Splunku. Tím získáme online monitoring. Navíc si budeme moci jednoduše psát vlastní minitorovací sondy. Pro sledování dlouhodobějších trendů si uděláme konektor, kterým budeme dávat data k nám do GoodData.

    Poznámka: Na dalším CZJUG setkání, které proběhne 28.3.2011 budu mluvit o tom jak vývoj v cloudu a produktu, který je prodáván jako hostovaná služba, takzvaný SAAS (Software As A Service) změnil můj pohled a zažité stereotypy z enterprise Javy.

    pondělí 21. února 2011

    CZ Podcast 47 - Sclala

    Jsem rád, že se podařilo v rychlém sledu udělat další podcast na téma Scala. Informace, které jsem z Honzy Kotka vytahal mi docela otevřeli oči o Scale viz stabilita API, binarni komapatibilita atd. Na druhou stranu alespoň člověk ví, do čeho jde. Takže sosejte a poslouchejte, protože další podcasty se nám rýsujou na obzoru, tak ať se vám to tam nehromadí.

    středa 16. února 2011

    CZ Podcast 46 - Kanban

    Kanban je jedna z novějších agilních metodik, kterou jsme rozhodli používat namísto Scrumu, který nám tak úplně nevyhovoval. Co je to Kanban, proč nám vyhovuje víc než Scrum a další informace najdete v 46 dílu CZPodcastu. Sosejte, poslouchejte a pište.

    neděle 6. února 2011

    Proč selhává tradiční rozdělení rolí při vývoji

    Tradiční rozdělení rolí při softwárovém vývoji na architekta, designera, admina, databázového specialistu a k tomu manažera je největší omyl v softwárovém inženýrství. Proč fungují projekty, kde nejsou architekti, nejsou databázoví specialisté a naopak kolabují projekty jsou zastoupeny všechny tyhle role? Je to odcizením od podstaty vývoje a to je doručení finálního produktu. Rozškatulkováním na jednotlivé specializace rozmělňujeme obecné vnímání problému jako celku.

    Funguje to v reálném životě? Nepřenositelnost neuznávám, paralely jsou výborné. Přijde mi, že to je jako kdybychom měli lidi, co napíšou recept jak upéct dort. Ten recept dostane jiná skupina, která ho nějak pochopí, ukvedlá těsto a hodí ho do trouby nastaví pečení. Potom je tu další skupina lidí, co sleduje jaká je v troubě teplota a jestli se to náhodou nespálí. Na talíř to dostane úplně jiná skupina lidi, kteří jsou diabetici, takže ten dort stejně nesežerou. Ťukám si na hlavu, protože o vaření a potažmo pečení dortu vím kulové, ale i s takto omezenou znalostí si představím jednoho kuchtíka, který je tam od pochopení receptu až po ochutnání finální podoby.

    Paralelu s pekárnou, kde se pečou rohlíky jak na běžícím pásu bych nehledal, protože softwarový vývoj je cokoliv jiného jenom ne sekání stejných výrobků.

    Dalším problémem je něco jako kolektivní vlastnictví. Každá role vlastní jenom kousek problému a ani jí vlastně nezajímá, jaké problémy bude mít ta role vedle. Práce designera končí u návrhu uživatelského rozhraní, architekt navrhne něco co vývojáři nejsou schopni naimplementovat a nebo je to totálně mimo mísu, vývojáři udělají něco k čemu nebudou mít Operations nástroje. K tomu musíme započítat ještě manažera, což je většinou uměle vytvořená role, kterou máme jenom protože jsme rozdělili vývoj do roli a obsadili do nich lidi, kteří spolu nejsou schopni komunikovat. Je to začarované kolečko, kde se problémy přehazují jako horké kaštany.

    Přijde mi jako kdyby tenhle systém s rolemi vymyslel někdo komu se nepřepnul mozek z návrhu programu do reálného světa. My to role designujeme jako objekty, kde má každý svojí odpovědnost a svoje rozhraní. Bohužel tedy bohudík na rozdíl od programu nemáme v tom reálném světě pod rukou ten kontext, ve kterém hrají roli takové drobnosti jako mezilidské vztahy, osobní ambice a x dalších faktorů.

    Všechno špatně a zpátky na stromy přátelé.

    Klíčem k úspěšnému vývoji, pardon jedním z klíčů, je nemít na všechno specialistu naopak. Je jedna role, ta role se jmenuje softwarový vývojář. Ten je schopen kód navrhnout, otestovat, nasadit, udržovat a hasit v něm problémy pokud nastanou. Samozřejmě máme softwarové vývojáře, kteří jsou zkušení a nezkušení, záměrně nepoužívám slovo horší nebo lepší. Proto vývojář většinou nepracuje sám, ale vždycky s někým spolupracuje. Softwarové vývojáře skládám do větších celků, skupin, aby byl ten mix mezi tím co mají doručit a tím co skupina umí byl vyvážený.

    Samozřejmě stále existují lidé, kteří nejsou v první řadě softwarový vývojáři, ale jsou to lidé zodpovědní a nebo lépe řečeno garanti za určité oblasti. Anarchie to může být jenom částečná.

    Velice se mi líbí to o co se pokoušíme v GoodData, ještě nemůžu napsat, že děláme, ale jsme na cestě a tomu se říká DevOps. Pokud jste od DevOps neslyšeli, pak třeba podcast 39. Neni to úplné splynutí rolí, ale ten rozdíl mezi některými rolemi to skrývá úplně. Jeden člověk jde s kódem od jeho návrhu (prototypu), přes implementaci, otestování až po deployment. Což má samozřejmě za následek takové detaily, jako že logování, které by normálně odfláknul, vyprecizuje, protože jediné co bude mít při řešení problému bude právě ten log, do kterého zapisuje. Stejné je to s deploymentem a nebo například databázw, u které bude muset zajistit ve spoluprací s někým kdo je zodpovědný za produkci její zálohování.

    neděle 30. ledna 2011

    Oracle se rozhodl nechat umřít NetBeans

    Udělal již Oracle něco, cokoliv, čím by nenaštval alespoň část Java komunity? V poslední době například nechutné tahanice kolem Hudsonu, které vyústily v odchod hlavního vývojáře. To že je Oraclu nějaká komunita šum a fuk je jasné i těm největším optimistům, ale jsou rozhodnutí, které nedávají z dlouhodobého pohledu smysl vůbec. Jedním z nich je vyhození podpory Ruby on Rails z NetBeans, to už je totální nonsense, který mi hlava nebere. Ačkoliv třeba Borůvek v tom vidí samé klady pozitiva a jistoty. Oracle vsadil na špatného koně, pokud se tedy nerozhodnul nechat umřít NetBeans.

    Chápu, že firma asi nejde řídit amatérsky, jak to předváděl Sun viz tříletá agónie kolem JavaFX, ale zase se mu nedala upřít snaha o nějakou inovaci. Oracle je oproti tomu korporace, kde nikdy žádná inovace nemůže vzniknout. V tomhle světle dává zaříznutí RoR podpory perfektní smysl. Pro Oracle je strategická Java/JEE a podpora téhle technologie v IDE. Proč tedy podporovat Ruby on Rails s omezenými zdroji? Na první pohled žádný, pokud se ale podíváte na trendy, které se dějí ve vývoji kolem nás, pak už to není jednoznačné.

    Připomíná mi to situaci pět let zpátky, kdy Spring byl vysmívaný chudý příbuzný technologií ze světa JEE. Dneska je situace přesně opačná. A teď klíčová otázka, kdo bude dalším v řadě, kdo diametrálně změní povědomí jak dělat webové aplikace v Jave? Já jsem přesvědčený o tom, že to bude Ruby a Ruby on Rails a to pro většinu medium a small business společností.

    Proto mi nedává smysl zbavovat se technologie jako Ruby on Rails z IDE. Argument s tím, že RoR nemá s Javou nic společného neobstojí. Co JRuby? Dneska máte interoperabilitu Java a RoR na úrovni JVM. Tak kde je problém?

    Možná se pletu, ale Oracle vsadil na špatného koně. S JEE tady bude mít jistý pool uživatelů, ale s RoR mohl mít něco strategicky klíčového do budoucna. Z krátkodobého výhledu to dává smysl, z dlouhodobého nikoliv. Kdyby RoR nebylo tou další klíčovou technologií, kolik zdrojů by se na tom vypálilo? Protože až se ta vlak rozjede, Oracle už ho nechytí.

    Nepřeji lidem z NetBeans týmu nic špatného, ale tímhle krokem a kroky, které budou pravděpodobně následovat, Oracle odsuzuje NetBeans k pomalé smrti v zapomnění.

    čtvrtek 27. ledna 2011

    CZ Podcast 45 - Novinky, Java FX a Ruby on Rails 3

    V dalším díle máme jeden nekrolog a to u technologie Java FX, kterou jsme sledovali od počátku. Z výhledem do budoucna jsme se tentokrát bavili o Ruby on Rails. Takže stahujte, protože jsem zabojoval i s tou hlasitostí konečně.

    úterý 25. ledna 2011

    Prezentace NoSQL databáze z pohledu Java vývojáře

    Slajdy k povídání, které jsem měl v HUBu o NoSQL databázích z pohledu Java vývojáře. Touhle prezentací jsem chtěl ukázat jak zvrhlé věci běžně děláme v Jave k tomu, abychom si někam uložili a načetli data a co všechno k tomu potřebujeme a přitom to jde jednodušeji.

    neděle 23. ledna 2011

    Co mě testy naučily o mém kódu

    Návrat ke kořenům anebo někam kde jsem možná nebyl. Možná to bude znít jako klišé, a do jisté míry to i klišé i je, ale ukaž mi tvoje testy řeknu jaký jsi programátor. Jestli jsem někdy mluvil o tom, že většina testů je ze své podstaty povahy integračními a to pro většinu enterprise aplikací, které jsem psal, pak jsem se šeredně spletl. Dneska bych své tvrzení poupravil na, ano i integrační testy jsou potřeba, ale jejich počet by měl být alespoň u javového kódu výrazně v neprospěch tech jednotkových (unitových). Celý problém integračních testů, které vytváříme na úkor testů jednotkových je, že se nám zřejmě píší jednodušejí neboť náš kód je procedurální.

    Když jsem pracoval na HP SOA Sysyinet, měli jsme celé baterie integračních testů. Tam bych odhadoval poměr 95% ve prospěch integračních testů. Integrační test je pro mě pochopitelně i ten typ testu, který potřebuje ke své činnosti nastartování nějakého kontejneru např. aplikační kontext Springu. Díky bohu za ty testy, ale nejenom tyhle, ale i jiné integrační testy mají jinou vypovídající schopnost o kódu, který píšeme. Musel jsem napsat hodně kódu, abych v hlavě udělal tenhle mentální kotrmelec.

    Proč preferovat jednotkové testy

    Jednotkové testy nás nutí programovat objektově. Naopak integrační testy nás nenutí vůbec k ničemu. Vezmeme prostě tu nejvyšší vrstvu špaget a tu otestuju. To má samozřejmě za následek, že ten kód ve spod je hůře otestovaný, protože píšeme proti vrstvě horní. Ve chvíli kdy píšeme jednotkový test, tak nás to nutí mnohem více k tomu, abychom ty špagety rozpletly do objektů s vlastní odpovědností a ty potom jednoduše mockovali.

    Vedlejším produktem jednotkových testů je i to, že mnohem více přemýšlíte a jste nuceni přemýšlet o rozhraních mezi vašimi objekty. To jsou věci, které se vám při špagetovém programovaní poměrně zatemňují.

    Řekněme, že máme třidu, která bude stahovat RSS soubor z dané URL a bude vracet titulky příspěvků. Zjednodušený kód by možná vypadal nějak takhle.

     
    public class RssDownloader {
      public String[] getTitles(Url rssUrl) {
          HttpClient httpClient = new HttpClient();
          Xml xml = new Xml(httpClient.get(rssUrl)); 
          String titles[] = new String[];
          Node items[] = xml.getElements("Items");
          for(Node item : items) {
              titles.add(item.getTitle());
          }
          httpClient.close();
          return titles;  
      }    
    }
    

    Problém tohle třídy je v tom, že míchá dohromady dvě věci. Stažení RSS souboru a jeho zpracování. Správně bychom měli zpracování vlastního kódu držet pryč od toho odkud to XML pochází. Co když to RSS budeme najednou číst třeba z filesystému a nebo z databáze. Špatný objektový návrh, ale to si možná neuvědomíme dokud nebudeme ten kód chtít otestovat. Protože budete muset napsat integrační test a někde si nastartovat HTTP server. Ve skutečnosti, ale nechcete testovat HTTP klienta, ale otestovat, že z daného XML kód načte to co si představujeme.

    Správné řešení je proto zobrazeno na dalším obrázku.

    Pak je možné ono zpracování otestovat bez nutnosti mít integrační test. Můžete si dokonce, a měli byste si mockovat tu XML abstrakci. Samozřejmě ještě by šel HTTP klient přesunout jako instanční proměná třídy, zamockovat jej a pak testovat, že jej RssDownloader dobře používá (viz volání close metody).

    Na tomhle jednoduchém příkladu jsem ukázal, že objektové programování, v tomhle případě demonstrované na správném oddělení odpovědností objektů, vede k tomu, že kód můžeme pohodlně testovat jednotkovými testy.

    čtvrtek 13. ledna 2011

    Build z jedné hromady vs. releasovatelné celky

    Máte velký projekt? Trvá jeho build dlouho? Hodně projektů začíná tím, že máte pár zdrojových souborů, které zbuildovat znamená pár sekund. Postupem času narůstá jejich počet a vzájemné použití. Pokud je to projekt větší, nezbytný důsledek většiny softwarového vývoje, dochází k tomu, že zdrojové soubory začínáte členit do modulů s určitou odpovědností. Pokud takový projekt chcete vybuildovat, uděláte checkout z version control systému a pustíte váš buildovací systém. Tenhle přístup má kromě pár nesporných výhod i pár nevýhod, které se mohou ukázat jako docela zásadní. V tomto článku se pokusím nastínit alternativní přístup.

    Build z jedné hromady

    Build z jedné hromady je to co jsem popsal výše. Tedy zjednodušeně řečenou uděláte checkout a spustíte build. Tento přísup má jeden zásadní rys, to co vybuildujete přímo odpovídá tomu, co jste získali z verzovacího systému, případně lokálně upravili. Pokud máte modul A, která používá modul B, pak jakákoliv změnu, kterou uděláte v modulu B v uvozovkách pozná i modul A.

    Výhody

    Změna kódu na jednom místě se projeví ve všech částech zdrojových souborů, které leží na hromadě, kterou buildujeme.

    Nevýhody

    Pomalejší build, kvůli změně jednoho souboru musíme buildovat i zdrojové soubory, se kterými tato změna vůbec nesouvisí. Pokud modul A používá kromě modulu B navíc i modul C, pak musíte mít zkompilovaný modul C. Přestože jste tu třídu tohoto modulu léta neměnili, musíte jeh kompilovat neustále dokola. To asi znamená, že se v tom modulu pustí i testy. Tedy další zpomalení.

    Náchylné k prosakujícím chybám. Mějme náš příklad s modulem A, B a C. Pokud cokoliv změníme ve modulu B nebo C, tak tím můžeme efektivně rozbít i modul A. Pokud moduly leží v části projektu, za kterou nezodpovídáme, pak jakákoliv změna může mít vedlejší efekt, o kterém nemáme ani páru.

    Releasovatelné celky

    Releasovatelné celky jsou proti Buildu z jedné hromady přístupem, který je založen na tom, že vybuildované a z našeho pohledu stabilní celky našich vlastních modulů/komponent používáme v podstatě jako by se jednalo o kód třetích stran, od kterého máme jenom binárku, proti které můžeme buildovat. Tento přístup odstraňuje nevýhody Buildu z jedné hromady.

    Rychlost buildu. To co je stabilní a odladěné se nebuilduje, používáme existující binárku. Navíc různé verze jedné binárky lépe umožňují předcházet prosakujícím chybám. Jestliže modul A používá modul B a nějaký aktivista se pustí do jeho úprav, pak modul A může stále používat starší verzi modulu B aniž by se ho nové úpravy dotkly.

    Releasovatlné celky mají oproti Buildu z jedné hromady nevýhodu v tom, že potřebujete infrastrukturu, ve které držíte binárky vlastního kódu a také něco čím řídíte závislosti mezi moduly. V případě, že používáte Apache Maven, pak vám stačí vlastní repository, ve které budete mít vlastní artefakty (binárky z modulů). Řízení závislostí artefaktů je v Mavenu implicitní. Mimochodem to ovšem znamená, že vaše stabilní a odladěné artefakty nepoužíváte jako SNAPSHOTY, ale jako konkrétní verze.

    Pokud máte Maven a váš projekt má víc modulů z nichž některé se používají jako sdílené, pak bych rozhodně doporučoval použít přístup s releasovatelnými celky. Zrychlíte tím build proces včetně testů a pomůže vám to v předcházení prosakujících chyb.