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


23. dubna 2017

CAP Theorem

Byl jsem teď na pracovním pohovoru. Byl první a zatím ne poslední. Mám z toho takový dost rozpačitý pocit, ale o tom napíšu někdy příště. Zmiňuju to proto, že jsem z toho minimálně vytěžil téma článku.

O co šlo? Bylo mi na závěr pohovoru doporučeno, že pokud bych postoupil do druhého kola, tak bych si měl určitě nastudovat CAP Theorem.

Beru to jako příležitost a jelikož jsem zrovna četl výborný článek Techniques for Efficiently Learning Programming Languages, volím formu blog postu. A rovnou na rovinu říkám, že CAP Theorem je pro mne... ehm, theorem, teoretické tvrzení, se kterým nemám praktickou zkušenost. Takže budu rád, pokud mne v komentářích opravíte, nebo doplníte.

CAP Theorem à la Wikipedia

Začněme definicí z Wikipedie: CAP Theorem říká, že "pro distribuovaný počítačový systém je nemožné poskytovat simultáně více, než dvě ze tří následujících garancí:"
  • Konzistence (Consistency) - systém vrátí při každém čtení poslední zápis.
  • Dostupnost (Availability) - systém vrátí pro každý požadavek odpověď, nicméně bez garance, že jde o poslední zápis.
  • Tolerance rozdělení (Partition Tolerance) - systém zpracovává informace navzdory tomu, že došlo k rozdělení sítě (network partition), způsobené chybou komunikace mezi sub-systémy.

Wikipedia dále říká, že "žádný distribuovaný systém není imunní proti síťovému selhání, tudíž rozdělení sítě musí být tolerováno. V případě rozdělení, tak nastává volba mezi konzistencí a dostupností."

Zároveň je potřeba zdůraznit, že "volba mezi konzistencí a dostupností nastává pouze tehdy, pokud dojde k rozdělení; v ostatních případech není potřeba dělat kompromis."

Tolik tedy Wikipedia. Samozřejmě, Wikipedia je skvělý začátek, kde začít hledat informace. Ale pak je lepší jít trochu blíže ke zdroji.

CAP Theorem originál

CAP Theorem byl původně prezentován Ericem Brewerem v roce 2000 na konferenci Principles of Distributed Computing, jako součást jeho key note Toward Robust Distributed Systems.

CAP Theorem, původní slide (zdroj: Toward Robust Distributed Systems)

Brewer tehdy prezentoval i další věci, které ho ke CAP Theoremu dovedly, a které nám dnes již přijdou samozřejmé. Třeba, že persistence v distribuovaném systému je složité téma, nebo rozdíl mezi ACID a BASE přístupem.

ACID vs. BASE (zdroj: Toward Robust Distributed Systems)

Pro mne nejzajímavější myšlenka celé key note zazní už na začátku: "Klasické distribuované systémy se zaměřují na výpočet, ne na data. To je ŠPATNĚ, výpočet je ta jednoduchá část."

Persistent State is HARD (zdroj: Toward Robust Distributed Systems)


CAP Theorem vědecky

Brewer tehdy ve své key note prezentoval CAP problém, jako theorem, tvrzení bez (matematického) důkazu (tedy přesněji jako doměnku - conjecture). Důkaz platnosti theoremu na sebe nechal čekat dva roky, kdy dva vědci z MIT - Seth Gilbert a Nancy Lynch - prokázali platnost theoremu, pomocí formálního modelu, ve své tezi Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services.

Při čtení tohoto dokumentu můžeme vidět určitý posun v chápání daného problému. Jednak se zde už nemluví o distribuovaných systémech, ale o webových službách. A pak je celý problém a jeho popis pozvednut na vyšší, abstraktnější úroveň. Za zmínku stojí asi hlavně dva modely, na kterých je důkaz postaven.

První model používá asynchronní síťový model, který nemá žádné hodiny (synchronizaci času) a jednotlivé síťové uzly se tak musejí rozhodovat pouze na základě přijatých zpráv a lokálních výpočtů.

Druhý model se více blíží reálnému světu - v částečně synchronním modelu (partially synchronous model) má každý node své hodiny, které mají všechny stejné tempo. Hodiny nicméně nejsou synchronizovány, takže můžou ukazovat různé hodnoty pro stejný reálný čas.

V závěrečném hodnocení modelů se píše: "V asynchronním modelu, kdy nejsou dostupné žádné hodiny, je nemožnost výsledku (impossibility result) velmi silná: je nemožné poskytovat konzistentní data a to i tehdy, pokud povolíme návrat zastaralých dat v případě ztracených zpráv. Ovšem v případě částečně synchronních modelů je možné dosáhnout praktického kompromisu mezi konzistencí a dostupností. Většina dnešních real-world systémů je nucena pracovat způsobem, kdy vrací maximum dat po většinu času.

CAP Theorem Re-visited

Naše povídání by nebylo úplné, kdybychom se nepodívali, jak to bylo dál - už jsme všichni velcí kluci a holky a víme, že žádné "žili šťastně až do smrti" neexistuje.

Ke CAP Theoremu se po 12 letech vrátil sám Eric Brewer v obsáhlém článku CAP Twelve Years Later: How the "Rules" Have Changed. Je to zajímavé čtení. Brewer tam - nijak překvapivě - říká, CAP Theorem byl velmi často nepochopen.

Vezměme si například důkaz, zmíněný v předešlé sekci - sice je formálně a matematicky správný, ale je postaven jen... na dvou nodech. To přece není realita systémů, kterých se CAP týká.

Jak říká Brewer: "Designéři systému by neměli slepě obětovat konzistenci, nebo dosupnost, pokud existuje rozdělení." Především je potřeba revidovat tvrdé pravidlo "2 ze 3". Jak v článku opakovaně zaznívá, vztah jednotlivých CAP aspektů je spektrum, nikdy to není binární. (Mimochodem, pokud jste si nevšimli, podívejte se ještě jednou výše na patičku slidu ACID vs. BASE - zaznělo to už tenkrát.)

Převážná část článku je věnována strategiím, jak řešit rozdělení sítě a následné zotavení se. Například jednou z možností zotavení jsou kompenzace, klasický nástroj dlouho-trvajících transakcí. Další možností je rekonciliace pomocí verzovacích vektorů (version vector). Někdy každá část partition použije různou strategii a tedy akcentuje jiný aspekt, tj. někdy dostupnost, jindy konzistenci.

Zotavení se z rozdělení (zdroj: CAP Twelve Years Later)


Co si z toho odnáším?

Bylo zábavné se teoreticky zorientovat v (momentálně) odlehlé oblasti mého oboru. Rád čtu a přemýšlím nad věcmi a zpracovat si to formou psaní je fajn. Na druhou stranu, nosnost a rozsáhlost dané oblasti pro mne zatím není tak přitažlivá, abych se pustil do nějakého praktického výzkumu. I když bych se třeba rád podíval na NoSQL databáze, které hodně těží z BASE principu.

Takže? Na pohovoru to už příště budu sypat z rukávu. A taky se budu trochu kritickým okem dívat na ty, co se budou ptát... už nebudou v takové převaze. A brát to trochu s nadhledem - svět není černobílý... je to spektrum.

Související externí články


Externí zdroje


10. března 2017

Jak dělám Java pohovor IV: Java workshop

Zcela bezkonkurenčně nejčtenějším zápisem na mém blogu je opus magnum Jak dělám Java pohovor. Jeho čtenost je řádově vyšší, než u zbytku veškerých textů. Ten článek už je skoro pět let starý a neodpovídá (mojí) realitě.

Věci, které za to stojí, se snažím vylepšovat, takže je v důsledku dělám jinak. A pokud máte to štěstí, že si můžete vybírat lidi k sobě do týmu, tak by vám na tom mělo záležet. Hodně. A tak si ty poslední tři, čtyři roky říkám, že bych měl svůj zápis zaktualizovat. Jak tedy dělám pohovor dnes?

Agenda Java interview

Agenda pohovoru zůstala hodně podobná:
  1. Představím se já.
  2. Prostor pro otázky kandidáta a/nebo představení společnosti, business domény, našeho oddělení a projektů.
  3. Diskuze nad kandidátovým projektem.
  4. Review kandidátova kódu.
  5. Java workshop
Drobnou změnou je bod číslo 3, diskuze nad kandidátovým projektem. Velkou a stěžejní změnou je pak titulní Java workshop.

Diskuze nad kandidátovým projektem

Dříve jsem v tomto bodě probíral kandidátovo CV, ale postupně se to vyvinulo v diskuzi nad konkrétní zkušeností. Přijde mi to zajímavější a přínosnější, než diskuze nad "suchou historií".

(Mimochodem, kandidáti mají neuvěřitelně zažitou historizaci svojí praxe. I když jim opakovaně explicitně zdůrazníte, že nechcete slyšet chronologicky odříkanou pracovní zkušenost, většinou vás naprosto ignorují a spustí naučený kolovrátek. Hanba vám HR a head-hunteři!)

V dnešní době se tedy ptám na projektovou zkušenost ve stylu:

"Mohl byste mi popsat Váš poslední, nebo poslední úspěšný projekt? Zajímají mě hlavně technické aspekty - mohl byste mi popsat a ideálně namalovat architekturu, či design vašeho vybraného projektu? Dále by mě zajímalo, jak byl projekt řízen, jaké role se na něm podílely a jaká byla Vaše role? Co třeba nějaké disciplíny SW inženýrství? Release management? Source code management? Issue tracking?"

Většinou se mi podaří kandidáta (přátelsky) přimět k namalování nějakého schématu na whiteboard. To má dva aspekty. Jednak se nad vizualizovanou architekturou/designem dobře diskutuje. A jednak poznáte, jak je na tom kandidát ohledně schopnosti vysvětlit technické věci, znalost modelování, uvědomění si "big picture", kontextu daného projektu a spoustu dalších věcí.

Ilustrační příklad whiteborard architektury

Java workshop

Když jsem pozměněnou podobu interview vymýšlel, chtěl jsem něco, co bude co nejblíž způsobu, jakým bych chtěl, abysme v týmu pracovali. Tedy: komunikovali spolu a uměli diskutovat nad kódem a designem. A samozřejmě... psali kód, ideálně s unit testy.

Začínám tím, že si s kandidátem sedneme vedle sebe a předložím mu - podle vybraného příkladu - následující obrázek (pro nadcházející odstavce, považujme za dané zadání návrhový vzor Observer):

Class diagram návrhového vzoru Observer

Zeptám se kandidáta, jestli zná UML a daný návrhový vzor. Pokud ano, tak ať jedno, druhé, či oboje vysvětlí. Pokud ne, řeknu nevadí a obojí vysvětlím já. Tahle část trvá krátce, většinou tak do pěti minut a jejím hlavním smyslem je porozumět zadání. A teď přijde to hlavní.

Otevřu notebook a... ukážu kandidátovi "skeleton" projektu natažený v IDE. Vypadá to nějak takhle:

Skeleton Java projektu v IDE

Jak je vidět na obrázku, i na UML diagramu výše, v projektu jsou dvě rozhraní a dvě implementační třídy. Ty konkrétní třídy "implementují" metody z interfaců, ale jsou prázdné. Čili, jde to zkompilovat, ale nic to nedělá.

Rozhraní a jeho prázdná implementace

Malá vsuvka, pokud vás to napadlo - v tom IDE to mám připravený v Eclipse a v IntelliJ IDEA. Dřív jsem to měl připravený i pro NetBeans, ale pak jsem to přestal udržovat - za ty čtyři roky mi na pohovor nepřišel nikdo, kdo by chtěl v NetBeansech dělat.

Zpátky k projektu. Kromě produkčního kódu jsou tam připravený - opět prázdný - třídy pro testy. Pokud by vás zajímalo, jak přesně ten startovní projekt v IDE vypadá, podívejte se na můj repositář na Bitbucketu: https://bitbucket.org/sw-samuraj/hiring/overview

Vysvětlím kandidátovi strukturu projektu a řeknu něco jako:

"Takže, tady máme prázdný projekt (pro návrhový vzor Observer) a zkusíme si ho naimplementovat. Pokud znáte, nebo jste dělal nějakou reálnou implementaci tohoto vzoru, můžete zkusit udělat ji. Anebo můžeme zkusit naimplementovat ten vzor jako takový, dejme tomu ukázkový příklad.

Záleží jen na Vás s čím začnete, jestli s implementací, nebo s testy a já jsem tady proto, abych Vám pomohl v případě problému. A v průběhu toho, jak budete psát, se Vás budu ptát na některé aspekty Vašeho kódu.

V projektu můžete cokoliv změnit, je zde jen jedno pravidlo - nesmíte nijak upravovat interfacy."

A je to. Kandidát začne psát, já se dívám, občas se na něco zeptám. Když se nám to zadrhne, jemně kandidáta navnadím, nebo popostrčím. Občas něco do kódu napíšu i já sám.

Každý kandidát je naprosto jiný. Někdo to celé vystřihne za půl hodiny i s pěknými unit testy. Někdo za stejnou dobu stačí vyrobit jednu metodu, která přidává prvky do kolekce. V průměru strávím touhle částí zhruba 40-50 minut.

A je to. Interview krátce zakončím a rozejdeme se.

Jak workshop vyhodnotit?

V momentě, kdy workshop skončí, už mám většinou jasno, jestli chci mít kandidáta v týmu, nebo ne. Někdy jsem na pochybách a je lepší se na to vyspat, nebo prodiskutovat s někým nezúčastněným.

Jinak vás asi zklamu - neprozradím vám žádné tajemství, jak z té cca hodiny programování udělat výsledek. Pro mne to byla dlouhá cesta, během které jsem mířil k jakémusi ideálu. Proto nepotřebuju nějaký checklist, abych věděl, jestli tam - s kandidátem - jsme, nebo ne.

Pokud vám minulý odstavec nedává smysl, nebo mu nerozumíte a přesto byste chtěli vědět, jak worshop vyhodnotit, zkuste se inspirovat těmihle aspekty:
  • Jak kandidát komunikuje? Během vysvětlování zadání a během programování.
  • Jak efektivně pracuje v IDE? (Na oblíbené IDE se ptám ještě před pohovorem, takže by to neměl být stresující faktor, že programuju v něčem nezvyklém.)
  • Jak hezky/čitelně/čistě/efektivně píše kód?
  • Jak designuje/strukturuje metody, třídy, testy?
  • Jak píše testy? Jestli vůbec. A kdy? Předtím, potom, zároveň?
  • Zajímají vás nějaké seniornější aspekty? Klidně se můžete ponořit do věcí, jako je specifická implementace některého Java API, nebo třeba do Reflection API (fakt, vyplyne to úplně přirozeně).
  • Chcete si ověřit znalost konkrétní verze Javy? 6, 7, 8? Žádný problém, jde to.
  • Jak kandidát komunikačně a znalostně reagoval na navržené alternativy?
Určitě vás napadne něco dalšího.

Co už nedělám

S odkazem na původní článek, bych vypíchnul věci, od kterých jsem upustil. V první řadě, už se nevrtám v CV. Ani před pohovorem, ani během. Životopis si většinou jen rychle proběhnu před phone screenem, jestli mě tam něco nezaujme - třeba jestli má kandidát zkušenost s (business) analýzou, architekturou, team leadingem apod. Prostě nějaký přesah za vývojařinu.

A potom, už nedávám hlavolam. I když tahle část bývala celkem zábavná, tak jsem ji odstranil - když jsem před těmi čtyřmi lety nově definoval, jak bych chtěl pohovor pojmout, zaměřil jsem se na to, aby to bylo co nejbližší denní realitě. A tam prostě vývojáři mechanické hlavolamy neřeší. (Čest výjimkám.)

Jak dál?

Pohovor tímto způsobem dělám poslední čtyři roky a musím přiznat, že po té době je to pro mne už příliš velká rutina. Chce to změnu. Zatím čekám na inspiraci. Je možné, že tenhle článek čtete v přelomovém období - takže jestli se někdy potkáme spolu na pohovoru, dost možná, že bude vypadat jinak.

No dobře, Java. Ale co .Net?

Z minulých dvou let mám zajímavou zkušenost. Poprvé v životě mě potkalo štěstí, že jsem se stal mentorem. Jako tím opravdovým, kdy se vztah mentor-mentee "samovolně", organicky vytvoří. Pikantní na tom je, že můj mentee není Javista... je to .Neťák!

No. Kromě jiného jsme spolu řešili také .Net pohovory. Z dnešního pohledu to vlastně nebylo nic těžkého - vzali jsme můj Java pohovor, vytvořili .Net workshop a bylo to.

Podstatné je, že to výborně fungovalo a dnes tak máme úspěšný nový .Net tým. Zkušenosti zkrátka občas přijdou ze strany, odkub byste to nečekali.

Předešlé díly


Související články

27. února 2017

Programátor -> Vývojář -> Software Engineer

Source: Wikipedia
Jak léta jdou, přemýšlím kontinuálně o cestě, kterou jsem urazil a kam směřuju. Jeden takový pohled na část takové cesty naznačuje titulek.

Dneska už jsem na té cestě dál - jsem v bodě, kdy už pár let "stavím" týmy. A byť nehledám lidi, kteří jsou mým odrazem - naopak, mám rád diverzitu a plánovaně, podvratně :-) ji aplikuju - podvědomě vybírám lidi, kteří na podobnou cestu aspirují a berou ji jako součást týmové kultury.

Téma je pro mě zajímavé - jak čistě myšlenkově, tak i prakticky. Pokud pracujete s týmy, tak kromě těch technických věcí, řešíte taky záležitosti jako rozvoj lidí, jejich vzrůstající finanční očekávání, ohodnocení jejich technické a jiné seniority atd.

Zároveň nebudu skrývat, že ne vždy se to podaří - přesvědčit, inspirovat lidi, že tohle je ta správná cesta. Občas vidíte potenciál, ale nepovede se vám dostat daného člověka z jeho bubliny. A někdy se prostě jen mýlíte a chybujete.

Korintským

Asociace, která mi naskočila, když jsem začal o tématu přemýšlet, pochází, možná překvapivě, z Bible - Pavlova listu Korintským. Je to taková ta pasáž, co se čte vždycky na svatbách... ale nebojte, nebudu vás zahrnovat láskou ;-) mám na mysli jinou větu:

Dokud jsem byl dítě, mluvil jsem jako dítě, myslel jsem jako dítě, měl jsem dětské názory; když jsem však dospěl, s dětinskými věcmi jsem se rozloučil.1. Korintským 13:11

Co mně přijde podstatné na tomhle citátu, je ten přechod - opuštění určité formativní fáze a přechod do další. A i když s tím občas bývají problémy, nejde o odvrhnutí předešlé fáze, naopak, ta by měla být integrována.

(A jen pro jistotu, protože vím, jak jsou lidi chytlavý - výběrem citátu jsem nechtěl říct, že – obecně – třeba programátoři jsou dětinští. Jasně, že občas jsou, ale není to generalizace. Ale což, ať si každý odnese, co potřebuje.)

Model

Jednoduchý model, který popisuje naše téma, vypadá jako tři soustředné kruhy. A tak jako cibule, krystal, či perla, rostou postupně schopnosti našeho hypotetického programátora.

Skill model

Programátor

Znáte termín code monkey? Je to většinou nelichotivý termín, i když občas se k němu někdo hrdě hlásí (většinou nápisem na tričku). Mě se líbí definice z Urban Dictionary, která dobře vystihuje, co si pod termínem programátor představuju já:

"A programmer who isn't actually involved in any aspect of conceptual or design work, but simply writes code to specifications given."

Programátor je pro mne člověk, který - s ohledem na svou senioritu - dobře rozumí úzce vymezené technické oblasti. Hranice je zpravidla definována daným programovacím jazykem a v dnešní době také stále více nějakým přidruženým frameworkem. Cokoliv za algoritmy, technickými aspekty jazyka a lokální kompilací (je-li potřeba), je mimo oblast zájmu a zodpovědnosti programátora - jeho práce končí komitem do verzovacího systému.

Taková úzce vymezená specializace nemusí být na škodu - záleží, jaká je vaše doména, role v týmu, rozsah projektu ad. Na druhou stranu, moderní softwarový vývoj vyžaduje daleko komplexnější spektrum schopností a mít v týmu "pouhého" programátora pak často znamená více zatížit ostatní členy týmu a  přenést na ně část činností, jež u programátora "padají pod stůl".

Vývojář

Vývojář je už o úroveň dál. Že dobře rozumí svému programovacímu jazyku, je samozřejmost. Stejně tak dobře ovladá sadu souvisejících knihoven a frameworků, jež tvoří jeho technickou doménu.

Jako velmi volný příklad si představme mlhavý termín Backend Developer. Umí asi nějakou "business logiku", řekněme v případě Javy někdo s CDI/EJB, nebo Spring+AOP+Transakce, plus nějakou tu persistenci, tudíž JPA/Hibernate/MyBatis/JDBC. A trošku té security.

Takže to je samozřejmost. Ale je to ještě něco navíc - větší, či menší povědomí, jak to spolu souvisí, nejen interně, ale i za hranice toho "backendu". Plus troška "toho designu" a zase - jak na úrovni kódu, tak na úrovni komponent.

No a pak... pozvolné přibližování se k oblasti SW inženýrství: vědět něco o buildování, možná i nějaký ten release management, něco praxe a teorie testování (aspoň unit a functional), source code management, issue tracking, atd.

To hlavní, co odděluje vývojáře od programátora? Schopnost vidět v technické oblasti "big picture".

Software Engineer

A co SW inženýr? Ovšem, je to dobrý vývojář. Ale ovládá také většinu souvisejících oblastí, potřebných pro kvalitní a moderní vývoj softwaru:

Ale v první řadě - a to už je moje velmi subjektivní spekulace - rozumí byznisově tomu, proč daný úkol dělá a proč ho dělá daným způsobem (což je, vzhledem k mé zkušenosti, velmi, velmi vzácné). Přece jenom, je to inženýr a jeho prvotním úkolem je řešit problémy. Reálné problémy.

Jediná cesta?

Na internetu a v knihách se vedou dlouhé diskuze, co je a co není SW inženýrství, ba, jestli vůbec něco jako SW inženýrství existuje. Moje zkušenost je, že na každém projektu je potřeba vždy a znova vybudovat terminologii. Význam a kontext konkrétního termínu se neustále proměňuje a podstatné je, abyste si s kolegy v týmu rozuměli.

Pro mne je software inženýr období a stav, kam jsem se dostal po téměř 20 letech programování. Šel jsem po výše naznačené cestě. Ta vaše bude zcela určitě jiná. O cestě, co jste urazili můžete vyprávět, ale zkušenosti jsou jenom vaše.

Je to realistické?

Samozřejmě, život není o zjednodušujících modelech a ideálních okolnostech. Skutečný svět je špinavý, divoký a ne-soustředný. V realitě tak vidíme spoustu prolínání vyše řečených rolí, jež se častokrát mění v čase i v rámci krátkého období, jako je třeba iterace, nebo projekt. A pak, existuje spoustu odlišností, jež nejsou výjimkami, ale spíše úplně jinými koncepty a přístupy. Diverzita života se mi vždycky líbila.

11. prosince 2016

Merge dvou tabulek v Pythonu

Aktuálně studuju na Coursera kurz Introduction to Data Science in Python a jak se to někdy hezky sejde, naskytla se mi v práci možnost to rovnou použít v praxi.

Rozhodně nejde o nic světoborného - prostě potřebuju dát dohromady dvě tabulky, trochu pošolichat data a udělat hierarchický index. Asi by to šlo udělat i v něčem jiném a možná jednodušeji. Ale Python mám rád a je to zábava si trochu zablbnout.

Následující zápisek je zdokumentování postupu, který by se mi mohl někdy v budoucnu hodit. Pokud jste někdo větší borec v Pythonu - tj. nepíšete v něm jen jednou za pár let, jako já - budu rád, když mi poradíte nějaké zlepšení.

O co jde?

Určitě jste se s tím už setkali. Máte určitý nástroj, od kterého byste čekali celkem jednoduchou funkcionalitu a on ji, z nějakého důvodu, nemá. Takže skončíte u exportu dat a nějaké externí úpravy.

To je náš výchozí bod - máme dvě vyexportované tabulky, formát souboru v tomhle případě nehraje roli. Může to být CSV, Excel atd. Ty naše dvě vypadají takhle:

Tabulka CSD.csv

Tabulka RQS.csv

Co je pro nás podstatné, obě tabulky mají vzájemnou vazbu přes sloupec "Related issues", který odkazuje na identifikátor záznamu v druhé tabulce (sloupec "#"). Co není na první pohled zřejmé, záznamy v tabulce CSD (zelené záhlaví) mají vazbu 1:N na záznamy v tabulce RQS (červené záhlaví). Pro úplnost, vazba je obousměrná, nás ale zajímá jen směr CSD -> RQS.

No a potřebovali bychom z toho dostat tabulku, která vypadá takto (barevné rozlišení je pro přehlednost, který data kam patřily):

Výsledná tabulka matrix.xlsx

Povšimněte si v prvních dvou sloupcích hierarchického indexu ("CSD ID", "RQS ID"), který vyjadřuje vazbu 1:N.

Jak na to?

To, kolem čeho se točí výše zmíněný kurz a co jsem pro manipulaci s tabulkami použil, je knihovna pandas - open source nástroj pro datové struktury a datovou analýzu. Na svých stránkách píšou, že je easy-to-use a opravdu se s tím pěkně pracuje.


Základním elementem v pandas je DataFrame, ekvivalent dvourozměrného pole (se spoustou vychytávek). První krok je, převést naše tabulky do DataFrame. pandas mají spoustu možností, jak načíst externí data, my použijeme metodu read_csv:
import pandas as pd

csd = pd.read_csv('CSD.csv')
rqs = pd.read_csv('RQS.csv')
Zpracovávaná tabulka může být rozsáhlá, co do počtu sloupců, a protože výpis DataFramu na konzoli se defaultně zalamuje, může se hodit příkaz pro vypnutí této možnosti:
pd.set_option('display.expand_frame_repr', False)
Data máme načtená, můžeme je začít upravovat. První na řadě je tabulka CSD. Potřebujeme udělat následující úpravy:
  • Rozdělit data ze sloupce "Subject" do dvou samostatných sloupců pomocí oddělovače " > ".
  • Smazat sloupce, které v cílové tabulce nepotřebujeme.
  • Přejmenovat sloupce, aby v cílové tabulce dávali větší smysl.
csd[['CSD ID', 'Contract chapter']] = csd['Subject'].str.split(
        ' > ', expand=True)
csd.drop(['Category', 'Subject',
    'Assigned To', 'Target version',
    'Release', 'Related issues'], axis=1, inplace=True)
csd = csd.rename(columns={'Status': 'CSD status'})
Navíc uděláme jednu operaci, kterou budeme potřebovat později - přidáme si nový sloupec "numeric ID", podle kterého cílovou tabulku později setřídíme.
csd['numeric ID'] = csd['CSD ID'].str.replace('-', '').map(int)
Obdobné úpravy uděláme na tabulce RQS a můžeme se pustit do spojování. pandas nabízejí metodu merge, která funguje obdobně jako SQL join.
matrix = csd.merge(rqs, left_on='#',
        right_on='Related issues', how='left')
Dalším krokem je vytvoření hierarchického indexu:
matrix.set_index(['CSD ID', 'RQS ID'], inplace=True)
Následně setřídíme novou tabulku podle sloupce "numeric ID", který jsme si dočasně přidali v rámci úprav tabulky CSD. Sloupec po setřídění odstraníme. Smyslem téhle eskapády je setřídit tabulku numericky podle sloupce, kde jsou string hodnoty.
matrix = matrix.sort_values(by='numeric ID')
matrix.drop('numeric ID', axis=1, inplace=True)
Teď si ještě seřadíme sloupce, abychom se v cílové tabulce dobře orientovali:
cols = ['Contract chapter', 'CSD status',
        'Redmine ID', 'RQS description',
        'RQS status', 'Related issues']
matrix = matrix[cols]
A pozor! Finální příkaz... zapíšeme do Excelu, resp. jiného výstupního formátu. Máme hotovo.
matrix.to_excel('matrix.xlsx')

Kompletní skript

Celý skript je k dispozici na Bitbucketu jako snippet: matrix.py.

Jak nainstalovat pandas?

Nejjednodušší způsob, jak nainstalovat pandas a další spřízněné knihovny (třeba NumPy a dokonce i samotný Python) je package manager Miniconda. Stačí stáhnout instalátor pro váš operační systém a pak nainstalovat pandas příkazem:
conda install pandas