19. září 2017

Střípky z prototypování II: WebSockets

V úvodním díle jsme se dívali na jednoduchý prototyp - jak spojit dvě etablované webové technologie: Wicket a Spring. Bylo to takové zahřívací kolo, ještě o nic nešlo. Spíše o to, připravit si prototypovací platformu, než vyřešit zapeklitý technický problém.

V dnešním díle se podíváme na prototyp, jehož negativním výsledkem mohla být výměna GUI technologie, což by vedlo k refaktoringu cca 1/3 aplikace.

Kontext

πάντα χωρεῖ καὶ οὐδὲν μένει ~ Ἡράκλειτος
Jak říká Hérakleitos z Efesu: "všechno se mění a nic nezůstává stejné". To se tak stane, že zákazníkovi prodáte určité řešení. Všude - v odpovědi na RFQ, v kontraktu, na workshopech se zákazníkem - prezentujete, že GUI určité aplikace bude "wizard-like".

Vyberete technologie, nastřelíte aplikační prototyp a začnete vyvíjet business features. Vývojáři studují a postupně si osvojují nové technologie. Svět je krásný...

A pak přijde UX designer a hodí vám do toho vidle. Řekne, že uživatelé milují skrolování a cool, že jsou Single Page Aplications (SPA). A vy si uvědomíte, že GUI technologie, kterou jste zodpovědně vybrali (možná i nějaká bezesná noc tam byla), se se změnou paradigmatu není schopná vyrovnat.

Use Case

Cíl tohoto prototypu byl přímočarý: zpropagovat data, která přijdou na server z bezstavové RESTové služby do prohlížeče konkrétního uživatele a překreslit určitou část obrazovky, aby se tato data zobrazila. S nadsázkou jsem tomu říkal: přidat "reactive-like" chování.

Podmínkou samozřejmě bylo, aby zůstaly zachovány stávající technologie (tedy Spring a Wicket). Pro úplnost dodám, že Wicketovské komponenty jsou vždy stavové.


Implementace

Implementaci jde rozdělit do dvou kroků:
  1. Zpracování RESTového volání a propagaci dat do Wicket komponenty na serveru.
  2. Push dat ze serveru do konkrétního prohlížeče.

Observable Cache

První bod můžeme realizovat pomocí Observer patternu - do řešení přidáme další element, který zatím budeme nazývat observable cache (a více si o něm povíme v příštím díle). Observable cache nám bude fungovat jako synchronizační mechanizsmus:
  1. Wicket komponent se zarigistruje jako observer do observable cache.
  2. REST kontroler vloží data do observable cache.
  3. Observable cache notifikuje zaregistrované observery (wicket komponenty).


Jelikož observable cache je pro nás zatím abstraktní komponent, jehož technologie/implementace bude vybrána později (a navíc pro nás momentálně není podstatná), vytvoříme si ji pro začátek jako jednoduchou observable HashMapu. Pro začátek budeme chtít notifikace pro metody put a remove.


Následně zaregistrujeme Wicket komponentu (Panel) jako observera. Potřebná WebSocket logika půjde do metody update(). Prozatím ji necháme prázdnou, než probereme, jak se Wicket staví k WebSocketům.


Wicket a WebSockety

Je to taková matrjoška. Existuje WebSocket specifikace. Ta je implementována Java API for WebSocket (JSR 356). A Java API je pak obaleno Wicktovskou implementací/rozšířením. Wicketovská dokumentace je popsaná v referenční příručce v kapitole Native WebSockets. Některá témata zde chybí, ale je to dobrý začátek.

To, co Wicket k WebSocketům přidává a co také využijeme pro náš případ (a co také chybí v dokumentaci), je "broadcastování" WebSocket zpráv. V základě to umožňuje poslat WebSocket událost všem komponentům, které mají definované WebSocketBehavior. Komponent pak může přijatou událost dále filtrovat a rozhodnout se, jestli na ni reagovat.

To, že náše aplikace bude WebSocket-ready, nám zajistí náhrada klasického WicketFiltru za JavaxWebSocketFilter:


Tady trochu odbočím od WebSocketů k servletům. Aby WebSockety fungovaly, musí je podporovat servlet kontejner, ve kterém aplikace poběží (všechny moderní kontejnery by to měly umět).

V rámci popisovaných prototypů probíhá deployment jako součást buildu, do embedovaného servlet kontejneru, který nám poskytuje výborný Gradle plugin Gretty (k dispozici jsou Jetty a Tomcat). Bohužel, našel jsem tady pravděpodobně bug - WebSockety nefungují v embedovaném Jetty, takže je potřeba používat embedovaný Tomcat. (Ve standalone Jetty funguje všechno jak má, takže to bude problém Gretty.)

Zpátky k WebSocketům a Wicketu. Nyní, po notifikaci z observable cache, chceme z metody update broadcastovat WebSocketEvent a opět ji odchytit ve Wicket panelu, který budeme chtít překreslit. Data pro model komponentu si vytáhneme přímo z cache.


Pokud se podíváme na tento proces z hlediska kódu, potřebujeme ve Wicket komponentu aktualizovat pár věcí:
  1. V konstruktoru přidat na komponentu WebSocketBehavior.
  2. Z metody update broadcastovat IWebSocketPushMessage.
  3. V metodě onEvent zprávu přefiltrovat.
  4. Nechat komponent (nebo jeho část) překreslit.
  5. V rámci překreslení dojde ke znovu-načtení modelu.


Prototype repozitory

Pokud si budete chtít prototyp spustit a trochu si s ním pohrát, naklonujte si následující Bitbucket repository. Součástí prototypu je SoapUI projekt, s připraveným REST requestem, kterým si můžete poslat data do prohlížeče.

Klíčové třídy:

Poučení

Občas se ukáže, že řešení, které jsme odkládali jako nejzažší možné, je nakonec to správné. Nebo jediné funkční. Je potřeba zvážit potenciální důsledky - např. přepisování GUI vrstvy versus pozdější perfomance problémy (kolik WebSocket spojení bude v produkci otevřených? Jaký bude objem broadcastovaných zpráv? apod.)

Zvolili jsme na počátku dostatečně flexibilní technologii, aby uspokojila i nároky v úvodu zmiňovaného Hérakleita z Efesu? (Eh, chtěl jsem říci šíleného UX designera.) Malý, rychlý prototyp může dát na tyto otázky odpověď. Nebo aspoň vyznačit cestu, kudy ne.

Příště

V pokračování střípků se podíváme na to, čím nahradit observable cache. Můžete se těšit na sebevražedný deathmatch Neo4j vs. Infinispan.

Repository všech prototypů


Související články



16. září 2017

Smutná zpráva o stavu IT trhu

Aneb jak dělají technické pohovory jinde

Strávil jsem teď posledních pět měsíců hledáním nové práce. Nebylo to radostné období, bylo to tristní. Celkový dojem by se dal shrnout do jedné věty:

V oblasti IT jsme jen levná montovna aut.

Zaměstnavatelé (IT firmy) nehledají ani kreativní lidi, ani problem solvery, ani autonomní vývojáře. Nehledají lidi, kteří se konstantně učí a zlepšují. Ne. Místo toho hledají někoho, komu jeden bývalý kolega říkal láskyplně makáči. Občas jim někdo méně něžně říká lopaty. A já to řeknu natvrdo:

Většina firem u nás hledá programátory, které potřebují posadit rovnou k soustruhu, či k pásové výrobě.

A když to trochu víc zgeneralizuji, hledají lidi s fixním mindsetem. Zapomeňte na to, že byste se ještě někdy mohli něco naučit. Je potřeba, abyste sekali šroubky, jeden jako druhý. Jediným doporučením je, že jste navlas stejné šroubky dělali v minulosti (která skončila včera).

Silná slova, nebo jak to teda je?

Tak, bulvární úvod už máme za sebou, pojďme se na to podívat trochu objektivněji. V rámci možností. V tom, co jsem napsal v úvodu, se můžu těžce mýlit. Je to jenom odraz toho, jak interpretuji technická kola pohovorů, který jsem absolvoval. Hodně to reflektuje rozpor mezi tím, co firmy (a jejich techničtí lidé) říkají a jak reálně konají na pohovorech.

No a samozřejmě, ať to zazní hned na začátku - mám spoustu chyb a nejsem rytíř v blyštivé zbroji. Možná že celou situaci špatně čtu a prostě nedosahuji "standardních" nároků, které dnes IT trh vyžaduje. Možná mi jenom ujel vlak. Jako introvert, si to myslím polovinu času.

Moje reflexe je silně poznamenaná zpětnou vazbou, které se mi dostalo. Je po léta smutným faktem českého trhu, že v 80 % dostanete jako jediné vyjádření kultovní větu:
"Pro další jednání jsme upřednostnili uchazeče, kteří přesněji odpovídali aktuálním požadavkům pozice."
Nic proti rozhodnutí. Jen vám to tak nějak nepomůže, že jo? (A jsme zase u toho fixního mindsetu.)

Rozsah a forma

Dále neuvádím všechny pohovory, které jsem absolvoval, jen ty, kde proběhlo aspoň nějaké technické kolo (v libovolné formě). Uvádím vždy:
  • Anonymizovaný typ firmy a název pozice.
  • Krátký popis technického kola.
  • A pak asi to nejcenější - reflexi.
  • Formální, či neformální zpětná vazba od pohovorující strany.
  • Černobílé hodnocení mindsetu: fixed/growth.

Upozornění

Tento článek je převažně negativní. Proto bych zdůraznil, že je primárně o procesu, ne o lidech. Pokud se někde vyjadřuju rozporuplně o pohovorujících, jde o názorovou neshodu a nevypovídá to nic o jejich charakteru, nebo schopnostech.

Start-up inkubátor, System Engineer

Technický pohovor

Technický pohovor měl formu videocallu, cca 1-1,5 hodiny a povídali jsme si o tématech Computer Science, Software EngineeringDistributed Systems a Functional Programming. Ptali se mě mmj. na věci jako:

Potenciální další kolo bylo vědomostně ve stejném rozsahu, ale on-site, cca na 1/2 dne a "víc do hloubky".

Reflexe

Byl to můj první "start-up" pohovor v životě a tak mě dost zaskočilo tunelové vidění pohovorujících. Dvě třetiny otázek jsem nebyl schopný zodpovědět, protože:
  • jsem je v praxi nikdy nepotkal,
  • brali jsme je před 12 lety na škole a od té doby jsem je nikdy ani prakticky, ani teoreticky nepoužil,
  • nastudoval jsem si je před časem po večerech čistě z hobby-zájmu a nebyl je schopný z hlavy plynule přednést/vysvětlit (konkrétní příklad: před 5 lety jsem si nastudoval Hadoop, na blogu jsem se dostal jenom k zápisku o HDFS).

Z pohovoru i z následného (podmínečného) pozvání do dalšího kola jsem měl neodbytný dojem semestrální zkoušky - nastudovat, dostat zápočet, zapomenout. Když jsem se ptal, zda určité věci, na které se ptají, opravdu používají v praxi, otevřeně řekli, že ne, ale "tím si u pohovoru prošli všichni", tak by nebylo fér to v interview nemít i nadále.

Mým silným pocitem bylo, že si se mnou neví rady - jejich core business a technologie jsem neznal, zároveň "cítili", že "něco" vím (takže to nebylo na přímé zamítnutí), ale nevěděli, jak se k "tomu" dostat, jak se zaptat.

Přímým důsledkem tohoto pohovoru jsou dva články na blogu (CAP Theorem a Covariance & Contravariance) a téměř absolvovaný kurz Functional Programming Principles in Scala. Jaké bylo mé překvapení, když jsem zjistil že pohovorové otázky z funkcionálního programování kopírují témata z kurzu. Nic proti Martinu Oderskému - jeho kurz je výborný. Ale Scala je jen podmnožinou FP a některé věci nedávají v jiném jazyce smysl (třeba covariance/contravariance v Clojure).

Zpětná vazba

Musím ocenit následnou, celkem obšírnou zpětnou vazbu, kde byly v mailu rozebrány jednotlivé oblasti, s doporučením, na co bych se měl zaměřit, pokud bych chtěl pokračovat do dalšího kola.

Mindset

Fixed ⚔

Machine Learning start-up, Software Developer

Technický pohovor

Tady to nebyl úplně klasický přijímací proces - sešli jsme se opakovaně, na několik neformálních rozhovorů, kdy jsme se vzájemně přestavovali a poznávali. Nejvíce technické pak bylo řešení architektonicko-integračního zadání na white-board a firemní představení dvou projektů, hlavně z technického pohledu, kde jsem se mohl ptát spoustu technických otázek (předpokládám, že z toho se dá leccos poznat).

Reflexe

Velice příjemná forma pohovoru, postavená na vzájemném respektu (to není úplně samozřejmé). Musím ocenit přizpůsobení technické části mé aktuální roli a senioritě (celkem výjimečné).

Zpětná vazba

Vzhledem k neformálnosti a otevřené formě rozhovorů jsem měl pocit jakési "instantní" zpětné vazby.

Mindset

Growth ✔

Marketing eCommerce start-up, Functional Developer

Technický pohovor

Technický pohovor se skládal z rozhovoru o technických řešení z mé praxe a z implementace cons funkce... na papír.

Reflexe

Můj pohovorující byl evidentně velmi seniorní funkcionální programátor. Bohužel, jsme nenašli společnou platformu pro diskuzi o technických řešení. Pokud popisujete integraci přes RESTové služby na úrovni API a jste neustále přerušováni dotazy co přesně v ten moment chodí v hlavičkách na úrovni TCP protokolu a ani po 30 minutách se nedoberete bodu, kdy si sladíte terminologii; máte problém - pokud nejste schopni se domluvit na pohovoru, těžko to budete zvládat později v práci.

V programování na papír jsem extrémně slabý. Re-implementaci nízhoúrovňových funkcí považuji za ztrátu času (pokud zrovna na škole nestudujete předmět Algoritmy).

Zpětná vazba

Zpětná vazba byla, že pro core business firmy nejsem vhodný, ale "kdybych nebyl tak drahý", tak bych se mohl věnovat vedleší činnosti - přesýpání dat, klidně v Clojure, kde by to nebyl takový problém. Doufám, že se mi podařilo zachovat poker-face.

Mindset

Fixed ⚔

Český internetový hegemon, Developer of Advanced Systems

Technický pohovor

Prvně jsem online absolvoval Java test na Codility. Šlo o dvě úlohy, které bylo potřeba vyřešit a odevzdat během 90 minut. První úloha byla naimplementovat algoritmus na prokládání řetězců, něco jako: 12 + 98 = 1928. V druhé úloze jsem měl vymyslet(!) a naimplementovat algoritmus na počet kroků v geometrické spirále.

Pak jsem byl pozván on-site na pohovor s HR paní, v jehož závěru jsem psal na dedikovaném počítači kombinovaný Java-Python-C++ test, 75 otázek během 45 minut (nevím, jestli si správně pamatuji ten čas, každopádně to byla míň než minuta na otázku). Třetina otázek byla z Javy, třetina z Pythonu, třetina z C++.

Reflexe

Nemám slov. Tak špatně udělané technické kolo jsem během své kariéry ještě nezažil. Jsou to takové ty absurdity života, kdy přemýšlíte, jestli nejde o nějakou skrytou kameru. A když za váma zaklapnou dveře, máte potřebu se hystericky smát, abyste neztratili pojítko s vesmírem, který jste doposud obývali, než jste vsoupili do budovy.

Docela by mě zajímaly myšlenkové pochody člověka, který takovou frašku "designoval". Ještě bych tak akceptoval Codility jako jakýsi filtr pro ne-programátory. Byť vymýšlení algoritmu s tikajícíma hodinama v pozadí patří spíš do ranku "konkurz na McGyvera".

On-site test pak rezignuje na jakoukoliv konvenční racionalitu:
  • 2/3 testu nemají relaci jak ke kandidátově minulosti (v Pythonu jsem nikdy komerčně neprogramoval, v C++ ani jako hobby), tak potencionální budoucnosti (šlo o čistou Java pozici). Asi množstevní sleva, nebo snaha ušetřit.
  • Cca 40 sekund na otázku je asi z důvodů, aby kandidát nepřemýšlel, případně mechanicky tahal z trouby rozpečené polotovary.
  • Ad absurdum dotažené obskurnosti syntaxe daného jazyka ve stylu "kompilátor v hlavě intoxikované LSD".
  • Java array je "first class citizen", zapomeňte na cokoliv, co přišlo po Java 1.1. Třeba znalosti Javy 8 nejlíp otestujete tak, že se trváříte, že kolekce nikdy neexistovaly.

Zpětná vazba

Nesmím opomenout zdůraznit: nulová zpětná vazba:
"Pro další jednání jsme upřednostnili uchazeče, kteří přesněji odpovídali aktuálním požadavkům pozice."

Mindset

Extremely Fixed ⚔

Big Data start-up, Erlang Developer

Technický pohovor

Prvním krokem bylo programovací zadání, s deklarovanou náročností cca 1/2 dne, které mělo být odevzdáno do 24 hodin. Zadání bylo velmi vágní. K vypracovanému zadání přišly připomínky, které jsem měl zhruba do týdne doimplementovat a znovu odeslat. Odevzdání probíhalo mailem.

Následovalo on-site kolo, kdy jsem postupně mluvil s dvojicí sysadminů/operations, dvojicí architektů a dvojicí vývojářů (Java a Erlang).

Se sysadminy jsme si povídali hlavně o unixu a jak hledat problémy v produkčním prostředí. S architekty jsme si u white-boardu popovídali o jednom z mých projektů.

S Java vývojářem jsme si (tuším) povídali o vypracovaném zadání. Bohužel si víc nepamatuji, protože to všechno přebyl jeden detail (viz reflexe). S Erlang vývojářem to byla klasika - přinesl si papír a tužku. Už tušíte, že jo? Naimplementovali jsme si funkci na parsování regulárního výrazu.

Reflexe

Programovací zadání bylo celkem zajímavý - naimplementovat RESTovou službu s určitou business logikou. V čem jsme se podstatně rozcházeli, byla časová náročnost - z mého pochopení zadání by toto zvládnul naprogramovat za půl dne jenom superman. Já jsem na zadání, i s dopracováním, strávil 2 MD čistého času a i tak významně prioritizoval, co jen nastínit a co udělat v rozumné kvalitě.

Co mě dost otrávilo, bylo dopracování zadání - doimplementovat persistenci. Jakože cože?!? To je zadání pro studenty průmyslovky?!? Kdybych to měl řešit na pohovoru já, tak si o tom tváří v tvář popovídáme a za 5 minut se není o čem bavit. Aby to pro mne nebyla ztráta času, aspoň jsem si vyzkoušel verzování databáze přes Flyway (zůstalo nepovšimnuto).

Rozhovory se sysadminy a architekty byly celkem očekávatelné a nemám, co bych vytknul, či vyzvedl. S programátory to byla jiná. Začněme Javou. Napsal jsem v zadání kus takovýhleho kódu:


Když pominu design (brilantní kód zkrátka na první dobrou nepíšu), obsahuje tenhle kód jeden podstatný problém - iteruje se přes mapu, místo aby se k ní přistupovalo přes klíč. Pohovorující kolega mne na tento detail upozornil.
  • "Ano", říkám, "to je chyba".
  • "Byl jste ve stresu, když jste to psal?", ptá se.
  • "To bych ani neřekl, spíš jsem některým věcem dával nižší prioritu - postatný pro mne byl fungující, buildovatelný a spustitelný projekt.", vysvětluji.
  • "No dobře, ale z mapy se hodnoty vytahují přes klíče, to je přece základ.", vrací se k tématu.
  • "Ano", souhlasím, "byla to chyba."

Takhle to ještě chvilku pokračovalo, až se z toho stal nejsilnější pocit z interview - já jsem ten pro kterého je normální iterovat přes mapu, místo použití klíče. Je smutné, když se dva seniorní vývojáři baví o takové trivialitě tak dlouho a navíc spolu od počátku souhlasí. Ale možná, že mnou spáchaný hřích přehlušil veškerou další komunikaci.

No budiž, je to pohovor na Erlang programátora, nechme Javu odpočívat v pokoji. Takže: v praxi, stejně jako na pohovoru, je důležité:
  • Programovat na papír.
  • Re-implementovat esenciální core funkce jazyka (regex match)
  • Předpokládat, že funkcionální termíny jsou obecně platné, bez ohledu na kontext (přece když řeknu list, tak je jasné, že myslím spojitý seznam z cons buněk - žádné jiné typy seznamů přece neexistují).

Myslím, že o funkcionálních programátorech možná napíšu satirický článek.

Zpětná vazba

Hodně zklamaný jsem byl ze zpětné vazby - do pohovorů jsem investoval 2,5 MD čistého času (zadání + pohovory) a výsledkem byly dvě věty:
"Ačkoliv kolegové hodnotili setkání s Vámi jako zajímavé, bohužel do finálního kola budeme zvát jiného kandidáta. Dle kolegů se zcela nepotkávájí Vaše dosavadní znalosti s technickou profilací naší role a nebyli bychom schopni plně využít Vaše zkušenosti."
Vezměte si dení sazbu seniorního vývojáře za 2,5 MD a na druhou misku vah položte informaci, že jste "zajímavý". Neocenitelné, což?

Mindset

Fixed ⚔

Data Management produkt, Java Developer

Technický pohovor

Byl jsem požádán o zaslání odkazů na své public repository. Poslal jsem link na Bitbucket a GitHub a vypíchnul jsem dva hobby projekty (oba v Groovy).

Následovalo on-site technické kolo, kde dva seniorní vývojáři převáženě zodpovídali mé otázky o své práci. Překvapivě jsem se o jejich technickém řešení dozvěděl méně, než z předešlého rozhovoru s CTO, který mi nakreslil a vysvětlil blokové schéma. Vývojáři jen povídali a asi předpokládali, že už všechno vím.

Na prodiskutování mého kódu nezbyl čas, vybavuji si jen jednu povšechnou otázku na Groovy/Gradle. Na závěr byla půl hodina live-programování v Javě, implementace algoritmu procháze bludiště... v NetBeans IDE.

Reflexe

Že jsme neprobírali můj kód, považuji za chybu. Já chci před pohovorem kandidátův kód nejen vidět, ale hlavně ho s ním prodiskutovat. Jak říká Linus: "Talk is cheap. Show me your code." Ta diskuze je důležitá - pokud si z něčeho uděláte představu, aniž byste znali kontext, dojdete pravděpodobně k mylným závěrům.

Závěrečné live-programování považuji za druhou nejhorší zkušenost z popisovaných technických kol. Jako jediné pozitivní hodnotím, že jsem požadovaný algoritmus nemusel vymýšlet, ale byl mi vysvětlen.
  1. Programování probíhalo v NetBeans IDE, které nejen že nepoužívám já, ale ani nikdo v inkriminované společnosti! Na otázku: "Jak se v NetBeans dělá XYZ?", vám odpoví: "Nevím."
  2. Před-připravený "projekt" v IDE, byla jediná Java třída, zabírající vertikálně asi 5 obrazovek.
  3. Top třída obsahovala několik vnitřních tříd (i doménových). Trochu mi to připomnělo Javu před 10 a více lety, ještě než se rozmohly best-practices a Spring.
  4. Top třída vesele kombinovala static a non-static membery, kteří se vzájemně provalávali.
  5. Před-implementovaná část nahodile kombinovala procedurální a objektový přístup.
  6. Projekt neobsahoval žádnou přípravu pro unit testy, byť mi bylo řečeno, že pokud chci, můžu si testy napsat. Ano, pokud mám na zadání 30 minut, budu v IDE, který neznám rozcházet jak spustit unit testy (co třeba závislosti?).
  7. Pohovorující seděl celou dobu naproti mně a neviděl, co píšu. Prostě čekal, až skončím.

Přiznám se, na tomhle cvičení jsem pohořel. Ne že bych nic nenapsal, ale byl jsem trochu paralyzován. Asi na mě byl žalostný pohled, protože jsem byl požádán, abych napsal metody equals a toString. Nevěřícně jsem tak učinil.

Na závěr jsem byl požádán o zhodnocení. Přiznal jsem porážku a snažil se být diplomatický ve vysvětlení. Zakončil jsem tím, že daný kód, ten před-připravený projekt, by chtěl těžce zrefaktorovat.

Teď už být diplomatický být nemusím: Myslím, že je ostuda takový špatný kód ukázat kandidátovi na pohovoru. Je to první kód, který kandidát od firmy vidí. Pokud tato nemá problém ukázat něco takového, jako svou vizitku - rozumněj, něco čím můžu kandidáta nalákat - jak pak asi vypadají její produkční kódy?

Možná, že ve skuktečnosti píšou interně nesrovnatelně lepší kód. Potom ale hodně zanedbávají pohovory. Říkám si, jaké kandidáty asi chtějí tímto způsobem nabrat?

A ještě jedna poznámka - opět jedna z těch firem, kde musíte umět Java array jak když bičem mrská. Že od Java 5 používáte výhradně collections framework a v polích už nejste tak "fluent" jako kdysi, není argument.

Zpětná vazba

Zpětná vazba bylo dvojí. Jednak, hned po interview, mne zaskočila informace, že mne pohovorující kolegové "nepovažují za programátora". Budiž, názor je potřeba respektovat.

Následně jsem pak mailem dostal vyrozumění, které považujuza ilustrační příklad fixního mindsetu:
"Vaše zkušenosti a schopnosti jsou velice seniorní, bohužel však Vám nemůžeme nabídnout odpovídající pracovní pozici, kde byste tyto schopnosti uplatnil. To co děláte opravdu velmi dobře a na co se ve své práci zaměřujete, bohužel neodpovídá tomu, na co bychom se aktuálně v <jméno firmy> zaměřovali a není to v plánu ani v dohledné době, nebyli bychom tedy schopni Vám nabídnout odpovídající výzvy a začlenit Vás do některého z týmů."
Holt, s čím se jednou narodíš, s tím si musíš vystačit až do smrti. Proces učení se je iluze.

Mindset

Fixed ⚔

Velká (nejen) Java korporace, Big Data Engineer

Technický pohovor

Prvním kolem bylo zadání, které se skládalo ze tří částí:
  1. Programování RESTové služby s určitou business logikou.
  2. Doménový design jednoduché/zjednodušené business domény.
  3. Návrh solution pro zpracování nekonéčného streamu.

Na vypracování zadání jsem měl zhruba týden (minimálně 1. část), až dva. Řešení jsem komitoval do soukromé repository na GitHubu.

Druhé kolo bylo on-site, kde jsem se po dvojicích sešel s implementačním týmem: senior + standard(?) vývojáři, senior sysadmin + standard(?) vývojář.

S první dvojicí jsme si povídali o programovacích jazycích a pak jsem na white-boardu:
  • Vysvětlil řešení, na "které jsem pyšný" (granularita webových služeb v SOA řešení).
  • Navrhnul monitorování distribuované platformy.
  • Implementoval v Javě reverse Stringu, bez použití StringBuilder.reverse()

S druhou dvojicí jsem odpovídal na "linuxové" otázky a pak jsme si hlavně povídali, jak vypadá práce u nich.

Reflexe

Programovací zadání bylo celkem zajímavý a docela jsem si ho užil. Protože bylo velmi volné (to je rozdíl oproti vágní), tak jsem se rozšoupnul a půlku jsem naimplementoval ve Scale a půlku v Javě (že by potenciální článek?). Designové řešení doménového modelu bylo přímočaré, nic zvláštního.

Poslední část, streamové řešení, je pro mne exotické. Strávil jsem cca týden pročítáním internetu, abych se zorientoval a pak od stolu navrhl řešení v Clojure a jako zálohu Spark Streaming, s poznámkou nutného ověření (o Sparku nic nevím).

On-site práci na white-boardu hodnotím částečně pozitivně (popis mého řešení a monitorování), částečně negativně (programování a re-implementace core funkce). Programovat se má na počítači.

Jelikož většina pohovorujících měla z nějakého důvodu alespoň částečně načtený můj blog, cítil jsem během on-site pohovorů trochu přehnaný respekt, jenž nebyl adekvátní.

Zpětná vazba

První, krátkou zpětnou vazbu jsem dostal emailem po komitu prvních dvou částí zadání. Zbývající, opět krátkou, na začátku on-site interview.

Týden po on-site pohovoru jsem dostal další telefonický feedback. Řekl bych, že strukturovaný a vyvážený. Minimálně přesně pojmenoval mé slabší stránky.

Mindset

Growth ✔

Jak to zlepšit?

Mé hledání nové práce je momentálně u konce a doufám, že to minimálně dalších 5 let nebudu muset řešit. Shodou okolností a preferencí, se v blízké budoucnosti nebudu technickým hiringem zabývat. Jelikož ale pohory byly historicky vždy silným tématem tohoto blogu, měl bych aspoň doporučení (či zbožné přání):

Patero technického recruitera

1. Udělejte technické kolo co nejvíce praktické.

Nechte kandidáta vypracovat zadání. Udělejte s ním on-site workshop (jde to i přes Skype). Mějte zadání dostatečně volné, abyste kandidáta zbytečně netlačili do své škatulky. Nenuťte kandidáta programovat na white-board a už vůbec ne na papír! Neprogramujete děrné štítky.

2. Nechte si ukázat a vysvětlit kandidátův kód.

Uvidíte reálný kód, který vzniknul v určitém kontextu. Zadání, či workshop (předchozí bod) je pořád v laboratorních podmínkách, které nemusí každému sednout.

3. Testujte kandidáta na praktické věci, které reálně děláte.

Vykašlete se na re-implementaci algoritmů a core funkcí jazyka - nic podstatného pro váš business tím o kandidátovi nezjistíte. Naopak tím odfiltrujete slušné procento lidí, kteří excelují v něčem jiném. A zapomeňte na idiotské úlohy typu "kolik golfových míčků se vejde do letadla".

4. Nezkoumejte současný snapshot, hledejte projekci do budoucna.

Pokud něco neroste, je to pravděpodobně mrtvé. Současné kvality kandidáta jsou jen startovní bod. Snažte se najít jeho budoucí trajektorii.

5. Hledejte lidi s historií a schopností učit se.

Cílem nemusí být najít dokonalý technologický "match". Ale najít někoho flexibilního, kdo se potřebné věci rychle naučí, či do dané role doroste.
"You don’t hire for skills, you hire for attitude. You can always teach skills." ~ Simon Sinek

Související články


Související externí články


22. srpna 2017

Střípky z prototypování: Wicket, Spring, REST

Měl jsem to štěstí, že jsem se teď mohl několik týdnů věnovat prototypování. Štěstí, protože je to jeden z mých nejoblíbenějších aspektů softwarového inženýrství.

Všechny prototypy vedly k cílovému, zbrusu novému řešení. V kontextu naší firmy, by to normálně dělalo oddělení R&D, ale z kapacitních a projektových důvodů to skončilo v našem delivery týmu. Rád jsem se toho ujal.

Vzhledem k tomu, že by popis řešení a implementace zabraly mnoho článků, rozhodl jsem se vytáhnout jen několik zajímavějších fragmentů z následujících témat (a i ty rozdělím do dvou-tří zápisů):
  • REST contract-first
  • Wicket + Spring
  • Wicket + REST via Spring
  • WebSockets
  • Embedded Neo4j
  • Embedded Infinispan

REST contract-first

O tomhle tématu už jsem psal v článku REST contract-first: Swagger & Gradle. Zmiňuju ho pro úplnost, ať je to na jednom místě a také protože vychází ze stejného konceptu prototypu, jako všechny následující.

Wicket + Spring

Use case

Na počátku bylo slovo: udělejte novou aplikaci. Technologie nikdo neřešil - po pravdě, existovalo rozlehlé architektonické vakuum. Tak jsem se do toho vložil a na základě tehdejších informací a kontextu jsem vybral dvě technologie: Wicket na webové GUI a Spring na "všechno ostatní".

"Všechno ostatní" zde znamená:
  • REST služby a klienti
  • Business logika
  • (Potenciálně) persistence
  • (Finálně) security

Implementace

Wicket má se Springem dobré vztahy už po léta... i když, vo-cuď-pocuď. Zkrátka, bývávalo, že jste si ze Springu vzali jen to co potřebujete. Kdepak, už je to pěkně nenažraný cvalík, který si pozve spoustu bratříčků. A skloubit ho s něčím specifickým... je docela práce.

Nicméně. Způsob, jak propojit Wicket a Spring je dvojí - jde o to, kdo koho instancuje. První způsob - kdy Wicket instancuje Springovský kontext, ze kterého je potom možné injektovat do Wicketovských komponent přes anotaci @SpringBean - je dobře popsaný ve Wicketovské Reference Guide: Integration Wicket with Spring.

Druhý způsob je flexibilnější, avšak, není nikde moc zdokumentovaný a když už, tak jen pomocí konfigurace web.xml. Funguje to tak, že WicketFilter instancuje SpringWebApplicationFactory, která prohledá Springovský kontext a instancuje Wicketovskou WebApplication. Výhodou je, že se dá injektovat přímo do Wicket aplikace, což u předešlého způsobu nejde.

Pokud chceme oželet web.xml (ano, prosím!), vypadá konfigurace filtru takto:


Prozatím pomiňme, že filter rozšiřuje JavaxWebSocketFilter (viz sekce WebSockets), obyčejný WicketFilter postačí také.

Prototype repozitory


Pokud vám to neštymuje dohromady, klíčové jsou 3 třídy:

Poučení

Tady se žádné velké překvapení nekoná - funguje to stejně dobře, jako když jsem s Wicketem a Springem pracoval před osmi lety poprvé, jen se nám oba frameworky mocně posunuly ve verzích. Snad jen, že na takovém malém prototypu se jednoduše nacvičí přepnutí z jednoho typu instancování na druhý.

Wicket + REST via Spring

Use case

Spring a Wicket nám teď pěkně kohabitují. Ale chceme přidat RESTové služby - v současnosti všudypřítomná a triviální záležitost. Ne tak docela.

Spring nabízí REST buď v rámci Spring MVC, nebo Spring Boot. Přiznám se, tady mne dost zaskočilo, že Spring nenabízí RESTové služby samostatně, podobně jako ty SOAPovské (Spring WS). Nejsem expert na Spring, takže mi nejspíš něco schází, ale zatím to vidím, že dostává na frak buď modularita, nebo snadnost použití. Přitom jediný, co vlastně potřebuji je DispatcherServlet.

K tomu use casu - potřeboval jsem dedikované URL pro REST, které by neobsluhoval Wicket a zpracovávat/produkovat JSON zprávy.

Implementace

Řešení přišlo ve dvou krocích - zaregistrovat DispatcherServlet a podstrčit JSON mapování (z nějakého důvodu to Spring nedělá out-of-the-box). Dobral jsem se k řešení, které je sice funkční, ale rozhodně ne elegantní a možná i špatný design. (Zde bych byl vděčný za jakékoliv navedení na "správnou cestu".)

Zaregistrova DispatcherServlet jen tak přes @WebServlet annotaci mi nešlo - lítaly z toho tuny výjimek, protože ve Springovském kontextu chybělo spousty věcí ze Spring webové aplikace. Všechno to, co vám tam přidá anotace @EnableWebMvc. Tudy cesta nevedla.

Vyřešil jsem to dynamickým zaregistrováním servletu přes ServletRegistration, když jsem si přes Wicket vytáhnul ServletContext:


Druhý problém - přesvědčit Spring, aby chroustal JSON - mám vyřešený jen zpola: sice "zázračně" zafungovalo angažování komba AnnotationMethodHandlerAdapter a MappingJackson2HttpMessageConverter. Bohužel, je první třída @deprecated a to já nesnáším (pokudn není zbytí).

Spring doporučuje migrovat na RequestMappingHandlerAdapter, jenže k tomu chybí jakákoliv dokumentace a Google mlčí. Moje otázka na StackOverflow je také bez odpovědi.

Řešení, na které nejsem hrdý (účel světí prostředky):


Prototype repozitory


Klíčové třídy:

Poučení

Spring je fajn. Ale přijde mi po těch letech, že je míň flexibilní a narostl do stejného molocha jako Java EE. Pokud potřebujete něco specifického, budete se brodit Springovskými výjimkami a zoufale prohledávat StackOverflow. Mimochodem, další moje StackOverflow otázka o Springu je také nezodpovězena.

Říkám si, jestli to za to stojí, se přehrabovat tak hluboko ve vnitřnostech Springu, aby člověk implementoval relativně jednoduché věci. Trochu mi to zavání vendor lock-in: dobře si rozmyslet, jestli stavět heterogenní aplikaci, nebo použít homogenní Spring.

Příště

V pokračování střípků se podíváme na WebSockety a jak do "standardní" webové aplikace přidat "reactive-like" chování.

Repository všech prototypů


Související články


1. srpna 2017

Šest ctností softwarového inženýra

Čteme teď s dětmi výbornou knížku Buddhovy pohádky na dobrou noc. Kromě pohádek samotných je v úvodu knihy několik krátkých kapitol o tom, co je to Buddhismus, kdo byl Buddha apod. Tam mi padla do oka kapitola Šest ctností (pāramitā) a okamžitě mi začaly naskakovat paralely ze softwarového inženýrství.

1. Dána

Touha dávat každému, aniž bych očekával odměnu.
Moje subjektivní zkušenost bloggera je, že máte jen minimální zpětnou vazbu na své psaní a když už nějaká je, tak je neobjektivní - pokud nepíšete úplné blbosti, tak vás (zlomek) lidí buď pochválí, sem tam nějaký ten troll, nebo spam a občas vás někdo upozorní na chyby a nesrovnalosti.

Zkrátka, obsah blogu si žije svým životem a vy to nemůžete nijak ovlivnit. Pořád ještě žiju v iluzi, že občas mé psaní někomu pomůže, nebo ho aspoň pobaví. Není to jediný aspekt mého psaní, ale právě potřeba unilaterálního sdílení je silně přítomna.

Ze stejného ranku je také obecně knowledge sharing. Krystalickým příkladem je potom open source. Možná, že někdo publikuje svůj kód, protože očekává nějaké sociální benefity, třeba uznání. Ale řekl bych, že většinou je to záležitost kultury a osobního přístupu.

2. Šíla

Rozvíjení mravného chování.
Možná už jste to zažili - přišel za váma projekťák, či jiný manažer a (v lepším případě) vás požádal, nebo vám přímo přikázal něco, co je nemorální. Možná jen trošku, možná za hranicí slušnosti a možná i něco, co je polo/ne-legální.

Možná jste byli v týmu, který nebyl přátelský k ostatním stakeholderům. Možná ani ke členům vlastního týmu.

Nevytáhnu žádné eso z rukávu, jak tyhle věci řešit. Můžu potvrdit, že je to těžké, obstát se ctí. Abyste se za to nemuseli další léta stydět.

3. Kšánti

Trpělivost a schopnost zůstat klidný, zejména v problematických situacích.
V IT se točí hodně peněz. Možná jste dělali něco pro nějakou banku, nebo evropský/mezinárodní/globální projekt, kde šlo o miliony $/€. Když pak dojde na lámání chleba, lidi dokážou pěkně zdivočet.

A nemusí jít jen o velké peníze. Někdy stačí jen nevraživost v kanceláři, či spor o to, jestli se bude větrat, nebo pouštět klimatizace.

Tváří v tvář takovým malým a velkým problémům se pak poznají profesionálové a leadeři. A vlastně... zralí lidé. Kteří neztrácejí ze zřetele, proč jsou tady a co je cílem (projektu, schůzky apod.) a situaci věcně řeší. Nenechají se strhnout emocemi.

A ví, že některé věci potřebují čas. Však to znáte - žádný projekt/produkt nevyrostl přes noc.

4. Virjá

Nadšené úsilí, které podněcuje sílu a vytrvalost, jež jsou nezbytné pro pokračování na cestě bódhisattvy.
Projekty můžou být krátké a dlouhé. Produkty mají životnost ještě delší. Být smysluplným přispěvatelem v takové práci je běh na dlouhou trať. Ne náhodou beru maraton (a trénink na něj), jako dobrou paralelu softwarového projektu. Přijdou těžké chvíle, to je realita. Co vás z toho vytáhne, je motivace. Proč to děláte?
Winners never quit and quitters never win. ~ Vince Lombardi

5. Dhjána

Koncentrace nebo meditace, která rozvíjí duševní schopnost pevného záměru, aby naše činění přinášelo lepší výsledky.
Tak pravda, tady to asi úplně nesedí. Ale máme třeba coding kata. A určitě spoustu z vás kóduje po večerech, či po nocích, zkrátka ve svém volném čase. A pokud se u toho dostanete do flow, je to vlastně práce jako meditace.
Work is valuable. It will bring humbleness and silence. ~ Osho

6. Pradžňá

Moudrost, nejen coby intelektuální porozumění, ale také schopnost přímého vhledu do pravé podstaty skutečnosti.
Ve svém jádru je softwarové inženýrství velice racionální a opřené o data. Pokud opravdu dobře rozumíte nějakému jazyku, knihovně, frameworku, víte, jak to uvnitř pracuje. Ale svět projektů je daleko rozsáhlejší a nikdo ho není schopen obsáhnout v jeho celistvosti - znáte to podobenství o slepcích a slonovi.

Čím více je člověk expert v nějaké oblasti, tím více má vyvinutou intuici. Tedy - přímý vhled do problému. Vede k tomu dlouhá, trnitá a klikatá cesta.

Cesta bódhisattvy

Ne nadarmo se ultimátnímu odborníkovi v IT říká guru. Guruem se člověk nerodí, ale postupně stává. Cesta bódhisattvy (někdo, kdo odložil vlastní osvícení, aby pomohl druhým dosáhnout stejného stavu) může být jední ze způsobů, jak se na tuto úroveň dostat.

Související články


18. června 2017

Cesta samuraje, rok šestý

Normálně má blog SoftWare Samuraj narozeniny v květnu, ale myslím, že je každému jedno, že (téměř) tradiční retrospektiva přichází o měsíc později. A po dvou letech.

Wind of Change

Huš je to ták. Po čtyřech letech jsem došel k bodu, kdy je potřeba nejen změnit zaměstnání. Ale trochu i přesměrovat kariéru. A tak chodím poslední dva, tři měsíce po pohovorech. Je to tristní zkušenost, svět se pořád ještě nepoučil. No, myslím, že o tom bude výživný článek - zatím mám pracovní název: Smutná zpráva o stavu IT trhu.

Poslední dva roky jsem málo programoval. Téměř vůbec. Asi se to projevilo - poslední půlrok programuju hodně. V Pythonu, v Clojure, ve Scale, v Javě. Je to moc příjemné. A frustrující - přišel jsem o hodně praxe a potřebuju dohnat hodně znalostí.

Třeba Java 8. Kde kdo už ji rutinně mastí a já se teprve postupně učím, jak ty nové vlastnosti, na které jsem léta čekal, postupně uvádět do praxe. Přitom to není nic světoborného - lambdy jsou jen převlečené closures z Groovy (syntakticky spíš ze Scaly), streamy jsou vlastně jen sekvence z Clojure. Takže věci, které jsem v jiných jazycích používal už před pěti lety. Jen v tý Javě mi to nějak neleze z klávesnice. Dost možná je to tím, že ta Java syntaxe je dost ošklivá a užvaněná.

Nebo Clojure. Naposled jsem s ním dělal ve verzi 1.2, teď je ve verzi 1.8, téměř 1.9. Tudíž věci jako protocols, reducers, transducers a clojure.spec mě kompletně minuly. O boomu ClojureScriptu nemluvě. (Pro úplnost, protokoly ve verzi 1.2 už byly, ale ne v knize, podle které jsem studoval.)

Anebo Scala. Z ošklivého akademického kačátka se vyprofiloval cool jazyk, který si vydobyl svou úzkou a specifickou doménu v oblasti big data a paralelního zpracování. Tady se musím přiznat k určité kariérní chybě. Když jsem se před více než 8 lety rozhodoval, jaký další jazyk se naučím, volil jsem mezi Groovy a Scalou. Vybral jsem Groovy a z dnešního pohledu je jasné, že to byla slepá ulička.

Zatímco Groovy je dnes taková zestárlá Popelka, skriptovací holka pro všechno a jedinou rozumnou a rostoucí implementací je skvělý Gradle; tak Scala vyrostla do síly, kdy i na českém trhu jsou vám ochotni za ni zaplatit jako za hlavní jazyk. To se o jiném JVM jazyku říct nedá.

Minulost

Zmiňoval jsem, co mi uteklo. Co mi naopak minulé roky přinesly? Hlavně věci týkající se team leadingu. Postavil jsem si svůj vlastní team. Získal slušnou praxi v technické hiringu. Naučil jsem se dělat distribuované projekty. Naučil jsem se řídit distribuovaný tým (další potencionální článek).

Zlepšil jsem si mluvenou angličtinu a svoji španělštinu jsem dotáhnul na pracovní úroveň (jak říká LinkedIn, full working proficiency) - dělal jsem projekt, kde španělština byla oficiální jazyk. Všechny workshopy, specifikace a dokumentace byly ve španělštině. Trabajé como líder técnico, responsable de la implementación de la solución compleja en América Latina.

Mám za sebou dvě neúspešné implementace Kanbanu. Vina bude na mé straně, protože jsem nedokázal inspirovat team.

Mám za sebou úspěšné zavedení Hg Flow na několika projektech.

Mám za sebou neúspěšné budování teamové kultury. Opakovaně.

Vychoval jsem tři dobré technical leadery.

Nepodařilo se mi posunout tři juniory na seniornější úroveň.

Nalítal jsem kolem 230 000 km po čtyřech kontinentech.

Budoucnost

Chtěl bych si od team leadování odpočinout - aspoň rok se věnovat jen full time programování. Chtěl bych si odpočinout i od technických pohovorů - cítím rutinu a potřebu změny.

Chtěl bych změnit business doménu. Vypadnout z korporátního prostředí, ve kterém se pohybuji posledních 12 let (celou svou Java kariéru). Hodně mě teď zajímá oblast machine learningu a data science. Zatím jsem na úrovni čtení článků a okukování kurzů na Coursera.

Technicky, rád bych se odklonil od Javy. Ideálně, zůstal na JVM a dělal v Clojure, nebo aspoň ve Scale. Možná i něco jinýho - Erlang by byl fajn. Aspoň půl, na půl.

Chtěl bych potkat inspirativní lidi.

Související články


29. května 2017

Covariance & Contravariance

Myslím, že je dost pravděpodobné, že na univerzitě jste měli nějaký ten semestr statistiky (já jsem měl 4 😱 ) a tak by vám měl být aspoň lehce povědomý termín kovariance, který je vyjádřený vztahem:
cov(X, Y) = E[(X - E[X])(Y - E[Y])]
Tak o tom si dnes povídat nebudeme.

Místo toho bych si chtěl rozebrat téma, které jsem dostal jako otázku na nedávném pohovoru. (Jo, to byl ten pohovor, kvůli kterému jsem napsal článek CAP Theorem, takže to už je druhý zářez na pažbě.)

Když jsem tedy dostal otázku, jestli vím, co je kovariance a kontravariance, tak u prvního termínu mi zablikala kontrolka "mlhavé vzpomínky" (léta nepoužívaná statistika se projeví "určitým povědomím neurčitosti") a pak jsem na férovku přiznal, že termín kontravariance jsem nikdy neslyšel a nevím, co to je.

Poslední varování! Pokud vás neodradila rovnice v úvodu ani vás neodežene tento odstavec, vězte, že se dozvíte o temném zákoutí computer science, které vám asi bude prakticky k ničemu - já jsem tuto "záležitost" za 12 let v Javě nepoužil ani jednou. Na druhou stranu, je to aspekt, který je ve vašem jazyce trvale přítomen a dost možná jsou vám jeho praktické konsekvence známé.

Podivný případ s Array

Než se do toho pořádně pustíme, zkuste se podívat na následující kód v Javě, který se bez problémů zkompiluje. Co je na tom špatně a co se stane, když ho spustíme?


Asi jste správně odhalili, že na 9. řádku micháme jablka s hruškama. Ehm, tedy Jedie se Sithama. Možná už ale nevíte, že zmíněný řádek vyhodí runtime výjimku ArrayStoreException.

Pokud bychom se podívali na ekvivalentní příklad ve Scale, tak se nám následující kód ani nezkompiluje, protože vyhodí kompilační chybu už na řádku 8.


To jsou věci! Vítejte ve světě variance.

3 sestry: Kovariance, Kontravariance a Invariance

Výše uvedené příklady demonstrují, že pole jsou v Javě kovariantní a ve Scale invariantní. Pojďme si na to posvítit. Všechno se to motá kolem pojmů subtyping (pozor, neplést s dědičností) a variance. Takže.

Variance je obecný pojem, který říká, jakým způsobem funguje subtyping u komplexních typů. Komplexním type je třeba generická kolekce, nebo funkce. Variance může být trojího druhu:
  • Invariance říká, že mezi komplexními typy není žádný vztah.
  • Kovariance umožňuje nahradit komplexní typ jeho podtypem.
  • Kontravariance umožňuje nahradit komplexní typ jeho nadtypem.

Pro úplnost je potřeba říct, že je tady ještě čtvrtá sestra: bivariance. Ale protože ji macecha ráda neměla, tak se s ní nebudeme zabývat.

Variance v kolekci

Pokud se vrátím k výše uvedenému příkladu v Javě - proč je pole kovariantní, když to pak působí problémy? Důvod je historický. Pole byla jedna z prvních kolekcí, které Java měla a jak ti starší z nás zažili, až do verze 5, neměla Java generika. (Mimochodem, jeden z lidí, kteří generiky do Javy přidávali, byl Martin Odersky, autor Scaly.)

Pole v Javě byly navrženy, aby podporovaly pole objektů a protože každá třída automaticky dědí z Object, tak jsou pole kovariantní. S příchodem generik to už nedává smysl a tak máme v Javě takovou dichotomii - pole jsou kovariantní (viz příklad nahoře), kdežto generické kolekce jsou invariantní.

Jak taková invariance v kolekci vypadá? Podívejme se na následující příklad seznamu v Javě:


Teď už by mělo být zřejmé, že ačkoliv typy v kolekci podporují subtyping (např. Jedi -> Powerful), tak kolekce samotné jsou invariantní: seznam List<Jedi> není podtypem List<Powerful>.

Immutable kolekce

Jak zrhuba říká Martin Odersky (tady hodně zjednodušuji), mutable kolekce by měly být invariantní, zatímco immutable kolekce můžou být kovariantní.

Kovariantní seznam si můžeme hezky ukázat ve Scale. Jelikož List je ve Scale immutable (ve skutečnosti je to klasická funkcionální struktura cons buněk). Právě immutabilita nám zajistí, že nás nepotká podobné runtime překvapení, jako u Java polí.


Tam, kde se nám Scala u polí bouřila (řádek 8), tak u seznamu ani necekne. Prostě kovariantní pohodička.

Jakou varianci má immutable Java?

Když se člověk dívá, jakou pěknou implementaci immutable kolekcí má Scala, tak ho napadne, jestli jsou také kovariantní immutable kolekce v Javě. Zklamu vás... nejsou.

V první řadě, immutable kolekce v Javě vůbec nejsou. To nejbližší, co se dá v Java kolekcích najít je Collections.unmodifiableList (ev. Map, Set, Collection atd.), což je ale jenom read-only pohled na interní, mutable list. Který je invariantní.

Pokud se podíváme na externí frameworky, immutable kolekce nabízí Google Guava. Když se na ně ale zaměříme z hlediska variance, tak jsou opět invariantní. Navíc, ošklivost builderu pro přidání elementu do kolekce se s funkcionální krásou cons nedá srovnávat:


To je všechno? Co funkce?

Povídání o varianci v kolekcích se nám trochu protáhlo, takže se nedostalo na to nejzajímavější - variance ve funkcích. Jako opravdu ve funkcích - Java se nám vrabčími krůčky přibližuje funkcionálnímu programování, takže bychom toto téma neměli minout.

Související články

  • Covariance & Contravariance II: Funkce (TBD)

9. května 2017

REST contract-first: Swagger & Gradle

U webových služeb mám rád přístup contract-first. Jsem 100% přesvědčen, že tak vzniká lepší design i lepší API.

V případě SOAP webových služeb je to celkem běžné. (Teda pokud webové služby "nedesignují" programátoři - to pak většinou skončí "vyzvracením" interního kódu na veřejnost.)

Ohledně REST-ových služeb mi to přijde jako minoritní způsob. To je jednak škoda a jednak problém. Ono to nakonec vždycky nějak funguje. Ale jen výjimečně pak vznikají API, které vývojáři milují.

Jak tedy na REST contract-first službu? Následuje popis, který jsem použil na stávajícím projektu a se kterým jsem - po vychytání (Swagger) much - spokojen.

Jak by to mělo fungovat?

Mám dost jasnou představu, jak by celistvý přístup contract-first měl fungovat. Pokud máte jiný postup, nebo se mnou nesouhlasíte, budu rád, když se podělíte v komentářích. Můj zobecněný přístup vypadá takto:
  1. Napsat kontrakt v nějaké rozumně standardizované a obecně přijímané specifikaci.
  2. Vygenerovat potřebný kód, zejména model a rozhraní (API).
  3. Vygenerovaný kód by měl být v adresáři, kde build tool očekává zdrojové kódy.
  4. Generování a kompilace vygenerovaného kódu je součástí standardního build lifecycle (není potřeba je spouštět samostatně).
  5. Ideálně, generování a kompilace probíhá jen tehdy, pokud došlo ke změně specifikace.
  6. Implementaci rozhraní si píšu sám, ručně.

Swagger

Swagger je soubor nástrojů, které se točí kolem OpenAPI specifikace. Za OpenAPI si můžete představit "něco jako WSDL pro REST". Pro naše potřeby nás budou zajímat dva nástroje: Swagger Editor pro napsání specifikace a Swagger Codegen pro generování kódu ze specifikace.

Swagger Editor

Swagger Editor je webový editor, který si můžete vyzkoušet on-line, ale pro reálnou práci bude lepší ho mít lokálně. Je trochu otravné, že kvůli tomu musíte nainstalovat Node.js, ale jinak má lokální verze víc šikovných funkcionalit.

Swagger Editor

Mimochodem, za celým Swaggerem stojí společnost SmartBear, která dělá SoapUI, takže očekávejte něco podobného - je to proklatě použitelné a v detailech mrzce nedotažené. A mizerná dokumentace.

Swagger specifikace

Swagger specifikace může mít dva formáty: JSON, nebo YAML. Jak na zmíněném projektu, tak v článku jsem zvolil YAML - jednak je to čitelnější pro lidi a pak, aspoň na projektu, to nebudou číst jenom programátoři.

Takže, contract-first. Začneme jednoduchou Swagger specifikací, která nám odpoví na otázku Života, Vesmíru a vůbec:


Swagger Codegen

Swagger Codegen je Java knihovna s CLI rozhraním. Swagger se chlubí, že pro generování kódu podporuje 20 serverových a 40 klientských řešení. Moje ukázka je ve Springu, ale klidně si vyberte svoji oblíbenou platformu.

Swagger Codegen CLI

Jak už jsem to naťuknul výše, ne všechno je ve Swaggeru perfektní - kolegové si dost stěžovali na kvalitu a použitelnost generovaných artefaktů pro JAX-RS a pro C#.

Já jsem sice s výsledkem spokojený a dělá to přesně, co jsem chtěl, ale bylo potřeba to poladit - strávil jsem cca 2 dny experimentováním, než jsem se dostal do stavu "akceptováno". Dva dny se mohou zdát hodně, ale jednak to byla zábava a jednak se to v blízké budoucnosti bohatě vrátí.

No, komand-lajna je sice boží, ale my se s ní patlat nebudeme, jsme přece profíci - použijeme Gradle.

Gradle

Pokud na Gradle Plugin Portálu zadáte heslo Swagger, vyjede vám 7 pluginů. Trochu jsem se bál, abych si nemusel napsat vlastní Gradle plugin, jako se mi to stalo u JAX-WS, protože žádný plugin nedělal, co jsem očekával. Ale nakonec po pročtení GitHubu a vyzkoušení jsem vybral plugin org.hidetake.swagger.generator, který šel rozumně ohnout pro moje potřeby.

Konfigurace zmíněného pluginu v build skriptu build.gradle může vypadat následovně (po ořezání ostatních věcí, které nás z hlediska Swaggeru nezajímají).


Podstatné věci na uvedeném skriptu:
  • V závislostech nám přibyla konfigurace swaggerCodegen.
  • Kvůli Swagger anotacím generovaným do API tříd je potřeba přidat závislost na swagger-annotations. To je sice otravný, ale asi nutná daň za použití Swaggeru. Naštěstí má ta knihovna jen 20 kB.
  • Cílová platforma, pro kterou generujeme, je definovaná atributem language.
  • Aby se nám negeneroval veškerý Swagger čurbes, omezíme generované artefakty atributem components, kdy říkáme, že chceme jenom model a api.
  • Adresář s vygenerovanými artefakty je potřeba přidat ke zdrojovým souborům pomocí sourceSets. (To už není plugin, ale čistý Gradle.)
  • A poslední řádek předsadí v lifecyclu task generateSwaggerCode před kompilaci Java kódu.

Bohužel, tím jsme s konfigurací ještě neskončili - tady to plugin mohl dotáhnout ještě dál. Sofistikovanější nastavení je potřeba dotáhnout v souboru config.json:


Tady stojí za zmínku dvě věci:
  • Jednak prázdný atribut sourceFolder, to aby nám Swagger nevygeneroval duplicitně zanořené adresáře.
  • A potom říkáme, že chceme vygenerovat jenom rozhraní (atribut interfaceOnly), aby nám Swagger rovnou negeneroval prázdné dummy kontrolery. (Možná použitelné jako stuby, pokud generujeme jenom jednorázově.)

A je to. Pokud spustíme build příkazem gradle build, dostaneme následující strukturu, kdy Swagger specifikace je v adresáři src/main/swagger a generovaný kód v adresáři src/generated/swagger:

Adresářová struktura projektu se Swagger definicí a generovaným kódem

Ukázkový projekt

Pro potřebu článku jsem vytvořil malý projekt na Bitbucketu, který vygeneruje potřebný kód a jde spustit v embeddovaném Jetty. Stačí naklonovan a spustit gradle jettyRun.

Měli byste ho vyzkoušet - minimálně vám odpoví na nejzákladnější otázku života. A vesmíru. A vůbec...

Související externí články