Vše stárne, včetně vědomostí

Chci si napsat malou aplikaci pro Android a jsem na rozcestí – Java nebo React Native? Volba byla na začátku jasná. Java, jak je u Androidí komunity zvykem od začátku. Přes všechna utrpení jsem si připravil prostředí a začal pracovat. Ale rychle jsem narazil. Neznám Javu a neznám Androidí API. Začal jsem tedy patlat dohromady všechno možné dle různých návodů na internetu, abych měl co potřebuji co nejdříve. Na konci dne jsem neměl vůbec nic a naopak spoustu otázek, jak udělám elementární věci jednodušeji, než jak jsem našel.

A pak přišla otázka: chci se to vůbec učit? Rád se učím nové věci. Rád zjišťuji, jak fungují jiné věci, jak fungují jinak. Jenže v tomto případě to není o tom jinak. Je to spíš další spousta znalostí, které za pár let budou k ničemu, a moc mne neposunou.

Vlastně jeden z důvodů, proč jsem na začátku vyřadil z možností React Native. React je tu chvíli, ze světa JavaScriptu, kde se celý ekosystém lehce promění během chvilky. Kdo si ještě vzpomene na Closure Library například? React však umím a docílím toho, co potřebuji, dnes, nikoliv za několik měsíců. Bez nutnosti muset se učit něco, co mi sebere spoustu času a za rok bude stejně k ničemu.

Takže si nakonec píšu aplikaci v React Native. Nikoliv protože chci zkoušet nejposlednější výstřelky. Ale protože nemá smysl se učit něco, co tu s námi dlouho nebude. V mém případě to bohužel prohrála „Androidí Java“.

O tomto tématu se píše i v blogpostu „Reflections of an "Old" Programmer“, který proletěl sociální sítěmi pár měsíců zpět. I vám doporučuji se vždy zamyslet, čemu věnujete čas. Existuje mnohem víc technologií, než stojí za to se naučit. Což jsem demonstroval na jednom mém paradoxním příběhu. :-)

How to merge git repositories into one keeping history

We had a lot of git repositories and sometimes we had to implement some new feature across more of them and keep in sync. Which is hard and means we actually need only one repository for our project. Well, Google has everything, like everything, in one huge repository, so why would you need separate git for every microservice, right?

Decision was made—merge it but with history. No merge without history. You know, it's pretty easy to split one big repository into more repositories. Other way around is much harder with challenges on the way. That's why I want to give you this help if you are facing same step.

Let's go! First of all you need to know some theory. Best way how to merge repositories is to prepare them in state that there will be no conflict. Let's say you have repositories A, B and C and want them to merge into X. Think about how you want them merge into X. Probably best option is to have in final solution repository X with directories A, B and C containing original repositories in new merged one. By now you probably get that idea—first step is to move all files into directories and then merge them.

You can do it simply with git mv but you will lose history. Actually you will not lose it, still you can blame files and see history with git log --follow. And that's why I don't like this solution. There is better one which will move all files into new directory and rewrite whole history as it would be for the whole time like that:

git filter-branch --index-filter \
    'git ls-files -s | sed "s-\t\"*-&FOLDER/-" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' --tag-name-filter cat -f -- --all

Important: watch end of second line, you have to change FOLDER for your name of directory.

When you do it with all your current repositories we can move on to merge it. It's kind of easy part. Add remote and pull it. Just repeat following for all your repositories:

git remote add src_repo_name src_repo_filepath
git pull src_repo_name
git remote rm src_repo_name

Important: src_repo_filepath is meant as just local path. I don't recommend to push those changes to origin. For historical purposes or if something goes wrong, it's good to have old repositories untached.

And now you have your new shiny merged repository, nice!

Yes, but… but what about other branches? You can do similar move for all branches as for master branches. There could be just two use cases when it's not enough or too complicated. For example two branches from two repositories I actually wanted as one branch in final one. I didn't want to do mistake with some cross-git merging (which can be done very easily) so I used different technique: make patches of affected commits and apply them in order as I need.

git format-patch -X HASH

Call this in original repository and branch. HASH is hash of latest commit and X means for how many commits you want to do patches. Then you will see patch files which you can apply. You can also modify and merge more commits into one.

git apply xxx.patch
git commit -m "..." --author ""

Second use case for this solution is when someone has local branch. You can “easily” merge public branches because you changed hashes and it matches but some colleague can have local branch and he will need to merge it as well. He can use this technique for that, just before making patches he needs to move all into same directory (all commits). For those purposes he doesn't have to run first slow command keeping history but faster one:

git subtree add --prefix=FOLDER

And that's it! Hope you will successfully merge it without problems.


Note: this is my first post in English. Sorry for those who prefer Czech. Don't worry I will still publish also some Czech posts. It will depend on how many people I will want to share it with. :-)

O čem je párové programování?

Když se řekne párování, někteří vidí dva lidi ztrácet čas u jednoho počítače, když by mohl každý dělat něco jiného. Jiní zase vidí dva lidi, kteří daný úkol zvládnou za polovinu času. Pravda může být v obou případech, ale oba případy jsou velmi vzácné. Častokrát je to někde mezi a pravá podstata párování leží jinde, než ve snaze mít úkol hotov rychleji.

Párování je především o investici do budoucna. O investici do kódu, lidí a týmu celkově.

Většinou všichni začnou řešit především, jak párování pomůže kódu, aniž si jsou plně vědomi dalších výhod. I já tak kdysi začal. A jak tedy pomůže kódu? Především tak, že ho vidí dva lidi po celou dobu vývoje. Tím se odstraní problém code review, kdy se při revizi už programátor kouká na výsledek a ne na průběžný tok myšlenek. Programátor může opomenout některý edge case, který bude při review o sto řádcích ještě více zahrabán, ale při programování to druhého hned trkne do očí.

Během párování jsou totiž dva. Jeden, který píše, a druhý, který diktuje aka driver. Běžný muž nezvládne dvě činnosti naráz :-) a tak se může plně věnovat psaní anebo přemýšlení nad všemi dopady okolo. Jinými slovy během psaní algoritmu těžko budete přemýšlet jaké to bude mít následky pro současný či dokonce budoucí kód. Častokrát dostanu nápad, zamyslím se, napíšu kód, pak se na výsledek podívám a zjistím, že jsem si neuvědomil jeden podstatný detail a musím lehce poupravit. V tom lepším případě, v horším až o pár dnů později po review, kde musím upravit mnohem víc věcí, které jsem na tom postavil. A na takové momenty tu je driver – mám instantní feedback na to, co dělám.

Dalo by se říct, že párování je velmi poctivé review. Ano, ale tím to zdaleka nekončí. Mnohem zajímavější je investice do lidí. Jeden z nejčastějších modelů párování je jeden zkušený a jeden méně zkušený programátor. Nemusí to znamenat nutně pouze junior a senior, i junior může mít roli toho zkušenějšího. Jde vždy o konkrétní dovednosti pro dokončení konkrétního úkolu, například programovací jazyk, framework, databáze, část aplikace, … A v této dvojici ten zkušenější v dané oblasti předává znalosti tomu druhému.

V takovém spojení samozřejmě úkol nebude hotov rychleji. Zkušenější programátor by to zvládl sám rychleji. Jenže ten druhý by sbíral zkušenosti sám mnohem déle. Tím je snad očividné, že z pohledu týmu se jedná o jasnou výhodu, investici. Navíc investice není ani tak velká – stejně by se dělalo code review. Při sečtení času psaní kódu, review a případné úpravy vs. párování nebude nijak velký rozdíl. Dokonce párování pomáhá i tomu zkušenějšímu. Programátoři nutně potřebují trénovat (sebe)prezentaci, protože spoustu skvělých lidí selhává při sdílení super nápadů či technologií. Plus nikdo ještě nesežral Šalamounovo hovno, nikdo neví vše a správně. Každý může mít malý nápad, který úplně změní pohled na věc.

Sám nespočítám všechny případy, kdy jsem po dlouhé době zjistil, jak se věci vlastně skutečně mají. Například dlouhou dobu jsem měl o některých technologiích mylné představy a přistupoval k nim za špatný konec. Všechno nakonec sice fungovalo a review prošlo. Kdybych však pároval a řekl moje myšlenky někomu nahlas, opravil by mne mnohem dřív a mohl jsem si ušetřit v některých případech spoustu času.

I z toho důvodu je někdy velmi výhodná kombinace senior a senior. Například jsme v práci měli úkol a byli jsme dva s lehce různými názory, jak to udělat. Vyžádal jsem si párování a tím se vše vyřešilo. Než se dlouze dohadovat, rovnou sednout a diskutovat při každém kroku. Doiterovali jsme do nějakého řešení uprostřed a i když tam budeme po odstupu času ještě ladit nějaké detaily, výsledek je mnohem lepší než kdyby dělal kdokoliv z nás sám.

Abych to shrnul, párování je investice. Díky párování se mnohem rychleji zaučí nováčci v týmu. Rychleji celý tým nasbírá spoustu potřebného know-how v týmu. Corové věci budou mnohem vyšší kvality. Nebude potřeba review, párování je totiž nejkvalitnější review. Rychleji lze vyřešit problém – kolikrát jste se už zasekli u řešení (nejen) bugu a potřebovali pomoc? Naučíte se různé postupy, tooly a zkratky, na které byste jinak nenarazili. Naučíte se prezentovat a učit. Samé pozitivní věci!

P.S.: Pokud nejste po půl dne párování naprosto vyřízení, děláte to blbě. Párovat nelze celý den, párujte klidně složité úkoly půl dne a pak sami odpočívejte u jednoduchých až primitivních úkolů.

Monolit nebo microservices? Ani jedno, services!

Velké kusy jsou zlo, ať žijí co nejmenší kousky!

To je s čím se často setkávám. Mám však jiný názor: praktičnost je mnohem důležitější než cokoliv jiného. Pokud budu mít úplně vše v jednom a bude problém takovou obří věc nasadit – je to špatně. Pokud budu mít spoustu malých krabiček, které budou různě mezi sebou komunikovat, sahat si na stejnou databázi bez plné automatizace včetně monitoringu – je to taky špatně.

Jakoby by existoval buď jen obrovský neohrabaný monolit nebo spoustu malých microservices. To ale není pravda. Existuje taky celá škála mezi těmito pojmy.

Spousta lidí utíká před monolitem, protože je tak nějak obecně známé, že mít aplikaci jako monolit je špatné. A sexy slovo je microservices a vrhají se po něm, aniž by vlastně věděli proč a přinášelo to nějaký dobrý užitek. Nepochopte mne špatně, nejsem proti microservices. Jen mají velmi málo opravdových využití a pokud mají, je potřeba jim velmi dobře rozumět a být na ně připraven. To samé platí pro monolit.

Drtivá většina naopak potřebuje něco uprostřed, říkejme tomu services. Něco, co logicky patří k sobě. Do takového logického celku může patřit databáze, cron joby, API, webovka a cokoliv dalšího, co utváří službu. A všechny tyto části mít v jednom repozitáři.

Z toho plyne spoustu výhod:

  • Jednu změnu lze provést v jednom commitu.
  • Snadněji se bude testovat, že je celek funkční.
  • Vždy vím, co je s čím kompatibilní – to co bylo dohromady v jednom commitu.
  • Nejsou tak problémy se závislostmi.
  • Deployment je pak mnohem přímočarejší a v případě nouze i rollback.

Pokud se služba rozbije na microservices, výše zmíněné výhody budou tu tam. Jakmile začnou dvě microservices komunikovat se stejnou databází, musí být někde specifikován model a pomocné funkce k němu. Jenže kde? Velice pravděpodobně v nějaké extra knihovně a bude se muset řešit závislost. Tím začne boj o to, co s čím je vlastně jak kompatibilní, z čehož vyplývá i pořadí nasazování. Testování napříč celou službou začne být náročné až téměř nemožné a bude vznikat spoustu falst-positiv či false-negativ, záleží na stylu testů. O jedné logické změně přes víc repositářů ani nemluvě.

Někteří si mohou všimnout, že co popisuji nejsou ve skutečnosti microservices. A budu jenom souhlasit. Microservices mají v definici nezávislost nasazování. Mezi lidi se však nějak dostalo „monolit je zlo a jediné správné jsou microservices, což je jeden proces napsaný v jednom jazyku“.

Aby se microservices v tom podání, které popisuji, obhájili, často slýchám či čtu podobné reakce:

Mohu nasazovat různé části různě často. Jenže to právě vede k častým problémům. Co s čím je vlastně kompatibilní, jak něco takového testovat, v jakém pořadí se musí instalovat, … Pokud tým něco takového opravdu nutně potřebuje, pak může mít smysl vynaložit energii do řešení těchto problémů. Jinak rozhodně ne.

Délka buildu je s microservices mnohem kratší. Koho to vlastně trápí? Nikdo to stejně ručně dělat nebude. V případě microservices je potřeba tak jako tak mít automatizaci na všechno, takže nikdo těch pár vteřin ani nepocítí.

Lehčí udržet čistotu a může pracovat na aplikaci více lidí. To lze i u monolitu. Množšství kódu bude úplně stejně, jen lehce jinak rozházen. Velikost repozitáře není důležitá, například Google má jeden obrovský úplně se vším!

Lze lépe škálovat. Jsou situace, kdy bude lehce snadnější situace u microservices, každopádně jak poběží aplikace na serveru by mi nemělo diktovat, jak si strukturovat aplikaci. Navíc s dnešními kontejnery rozhodně nechci, aby aplikace řešila kde a kolikrát běží. Chci jen aby uměla běhat kolikrát bude potřeba.

Je to jednodušší. To snad nyní už ani nepotřebuje komentář. :-)

Abych to shrnul, běžte do microservices, ale do těch správných. Pokud vás to svádí ke špatným, říkejte tomu prostě jen services.

Párové programování…

…není pro každého.

Aspoň co mi zkušenosti s párováním říkají, nejde využít párování s kýmkoliv. Kdysi jsem narazil na vtipný tweet, který má v sobě hodně pravdy: pokud se mají programátoři rádi, párují, pokud ne, dělají review. Ale není to jen o tom. Je tam ještě jedna důležitější věc. Většina programátorů jsou spíš takoví introverti a u párování se musí programátor otevřít. Musí ukázat své silné stránky, ale také ty slabé.

Při párování je pak hodně vidět, že vlastně někdo používá vim (či jiný editor, klidně i celé IDE) neefektivně. Že vlastně nevyužívá všech jeho možností a pluginů. Je hodně vidět, že vlastně debuguje stále „líně líně“, tzn. ne programátorsky líně, ale prostě líně – raději pomalu hází printy, než aby využil testů, nedej bože TDD, případně jiných zvyklostí konkrétního jazyka. A co je asi nejdůležitější – jsou vidět jeho myšlenkové pochody. Jak se ke všemu vlastně dobírá oklikou, až hloupě, dá se říci.

A to je to, co spoustu programátorů odrazuje od párování. Protože nechtějí, aby takové nedostatky byly vidět. Jednou jsem byl na večeři se začínající herečkou a barvitě mi vyprávěla, jak to mají herci těžké. Že se na place musí plně otevřít. Jsou vidět všechny chyby. Celá osobnost. Nic nezůstane u kolegů skryté. Jak je těžké něco takového přijmout a žít s tím. Hodně mi to připomínalo právě párování.

Každopádně když se přes tohle člověk dostane, stane se mnohem lepším programátorem. Párování má totiž spoustu výhod a vůbec se není potřeba bát svých chyb. Díky párování má člověk právě možnost se chyb zbavit. Je hodně literatury na spoustu témat, ale praktické věci se z knih nevykouká. Praktické věci je potřeba natrénovat a okoukat od ostatních.

Žádná kniha vám neřekne, jak máte ovládat editor. Žádná kniha vám neřekne, jak debugovat. Žádná kniha vám neřekne, jakým stylem tvořit kód. Záměrně píšu tvořit, protože na první dobrou se nikdy nic nenapíše. Vždy se nějak musí začít a postupně se dobrat k co nejlepšímu výsledku. Cesty jsou různé a každý si našel nějakou svou a té se drží. Jenže cest je spoustu a je velmi poučné sledovat někoho jiného s jinou cestu, kterou mne může obohatit.

Proto mám osobně párování rád. A proto také začnu nový nepravidelný seriál s články jakým stylem programuji. Takové párování přes blog posty. :-) Snad se bude líbit a někdo obohatí i obráceně mne, když uvidí, že něco dělám prapodivně (nebo i jen jinak). Pokud máte nějaký požadavek na konkrétní téma, nebojte se říct v komentářích! Stay tuned!

Jak jsem zápasil s Reactem

Před rokem jsem potřeboval napsat pro svoji aplikaci klientskou stranu jako SPA. Hodně se mluvilo (a stále mluví) o Reactu, tak jsem ho použil. Po roce, na rozdíl od Ruby, ho stále používám a jsem spokojen. Ale proběhlo několik velkých předělávek. Není vše tak růžové, jak se zdá.

React je totiž pouze V ze zkratky MVC. I když se zdá, že dělá víc, opravdu se jedná jen o view. A tuhle část zvládá naprosto perfektně! Jenže je potřeba taky M a C. Což je problém, protože jsme ve světe JavaScriptu a teď si vyberte mezi Fluxem, Refluxem, Reduxem, … prostě nějakým -uxem.

Podle vybraného uxu se vám při šťastném výběru zodpoví pár dalších otázek, jako například props vs. state či one store vs. many stores. Spíš očekávám, že to je něco, na co stejně narazíte. Sám jsem se držel Fluxu, neb je také od Facebooku. Ze zkušeností s JS světem jsem se raději držel toho, aby byla co největší šance kompatibility a zdrojů na internetu. Což znamená, že zmíněné otázky nejsou vyřešeny.

Krása Reactu či Fluxu spočívá zejména ve volnosti. Ono je to vlastně velice jednoduchý koncept a můžete ho použít kdekoliv. Buď jen koncept nebo celou knihovnu. Například Angular je skutečně plnohodnotný framework a můžete React využívat jen pro šablony. Tak lze teoreticky spojit výhody z obou světů. Prakticky nemám ozkoušené.

Nastupovat do vlaku Angular vs. Angular 2 se mi nechtělo, takže jsem stále zůstal u Reactu s Fluxem a snažil se otázky sám vyřešit. Vyzkoušel jsem mít spoustu malých storů a vše předávat statem. Vyzkoušel jsem mít jeden store a vše předávat propsy. Každý způsob má něco do sebe, ale popravdě čím větší aplikace byla, tím mi největší smysl dávalo najít se někde uprostřed.

Což tak nějak přirozeně sedí i s použitím React Routeru. Vždy jedna stránka má hlavní komponentu, které se předávají data ze storu statem a všem svým podkomponentám se dál data dostávají přes propsy. Funguje krásně, ale je hodně těžké najít tu správnou střední cestu. Každopádně pokud použití není přirozené, našel jsem chybu v architektuře komponent.

Abych však cestu neměl tak jednoduchou, prošel jsem si ještě jedním refaktorem – naverbováním Immutable.js, další knihovna od Facebooku. Bez ní totiž React postrádá jakýchkoliv výhod. To myslím vážně, jakýkoliv. Pomalé odezvy jsou nemyslitelné a zabijí jakoukoliv jinou výhodu. Reactí systém přerenderování pouze v případě potřeby funguje totiž pouze s Immutable.js a napsanými funkcemi shouldComponentUpdate. Bez toho nešlo aplikaci používat.

Což ale lehce komplikuje psaní kódu už tak komplikovanější prostředí. Například className či encType mne neskutečně irituje a těžce se debuguje, kde je chyba. Například chyba při renderování se v některých případech nenareportuje a když ano, traceback není úplně jasný.

Takovou čtvrtou velkou otázkou je, jak vlastně poskládat store? Co dovolit číst z venku, jak si udělat přehledné poslouchání na eventy a další. Opět jsem zkusil mít zveřejněné vše a nic. Jestli mít spíš kód ve storu či dispatcheru. Opět mi vyšla nejlépe střední cesta, od každého trochu dle potřeb.

Ale hej, React je fajn. Prototyp jsem měl hotov za den, pak už jsem jen zápasil s tím, jakým stylem napsat, aby bylo připravené pro mé potřeby. Navíc spoustu věcí jsem dostal zadarmo, například routing (react-router), klávesové zkratky (react-hotkeys) či Bootstrapcké komponenty (i když ty se vyžívají v neustálém předělávání a za čas strávený upgrady jsem mohl mít svoji vymazlenou stabilní verzi).

Závěrem: v práci jsem si vyzkoušel i Angular a díval se detailněji na Redux. Pokud bych psal další SPA, znovu bych sáhl po Reactu s Fluxem, Immutable.js a Routerem a šel cestou střední cesty, co se hlavních otázek týče. Nakonec je to jednoduché na použití a přehledný flow aplikace. Každopádně to neznamená React použít. Weby renderované na serveru mají stále své kouzlo. Vlastně je to, řekl bych, pro většinu webových aplikací ta lepší varianta.

setup.py nebo requirements.txt?

Pár dní zpět Armin Ronacher (tvůrce Flasku, Werkzeugu, Jinjy a dalších) tweetnul, ať se přestane používat requirements.txt a použije se setup.py s přepínačem --editable. Což rozpoutalo menší diskuzi na Twitteru i u nás v práci. U obou diskuzí se odkazovalo také na starší článek popisující rozdíl mezi těmito možnostnostmi.

A v čem je tedy ten setup.py lepší, resp. proč ho Armin doporučuje oproti requirements.txt?

Jak už je zmíněno v článku, setup.py má na starost seznam abstraktních závislostí. Dá se to přirovnat jako název tagu bez konkrétního Git repozitáře. Prostě chci requests ve verzi 2.10, ale neřeším, odkud se závislost stáhne. Může to být oficiální pypi.org, ale taky odkudkoliv jinud. Jenže to nestačí, pokud se v requests objeví chyba, kterou si opravím ve vlastním forku a nemohu čekat, než se dostane do upstreamu. Pak tu je requirements.txt, kam lze napsat odkud se má závislost stáhnout, klidně na konkrétní commit.

Jak tedy řešit takové případy bez requirements.txt?

Než odpovím, je potřeba si uvědomit složitější závislosti. Mám aplikaci a ta využívá například requests a knihovnu na stahování a parsování XML, která taky využívá requests. V případě vlastní verze requests bych pak musel nejen upravit závislost v mé aplikaci, ale také v knihovně, která vyžaduje requests. Což by vedlo k dalšímu forku jen kvůli jiné závislosti. Samozřejmě čím zanořenější závislosti budou, tím se stane komplikovanější něco takového udržovat.

Proto je lepší zůstat u abstraktních závislostí a při instalaci jen říkat, odkud se mají závislosti stahovat. Mohu buď využívat oficiální pypi nebo si udělat klidně i vlastní server. Při problému pak dám do svého registru balíků verzi, kterou potřebuji, a nemusím měnit všude závislosti na konkrétní.

Dobře, to mi dává smysl. Ale je to vhodné pro všechny?

Není. Pro střední a větší firmy určitě, pro malý soukromý projekt spíš ne. Navíc osobně mi přijde nejlepší aplikaci balit do prostředí, kde má běžet. Pokud je to Debian, pak nejlepší cesta je debianí balíček. S ním mohu řešit nejen Pythoní závislosti, ale také Céčkové a jiné závislosti a spoustu dalších věcí jako konfigurační soubory, logrotate, cron, … Vše hezky v jednom.

Ale princip zůstává stejný. Pořád je potřeba řešit vlastní verze, ať už z důvodu, že jsem opravil chybu, která ještě není mergnutá a publikovaná v upstreamu, či daná knihovna není ubalena jako deb balík. Opět nejlehčí mít vlastní debianí strom balíků.

No a co ten přepínač --editable?

Znáte takovou situaci, kdy upravíte knihovnu, nainstalujete lokálně, jdete vyzkoušet, zda aplikace si s ní rozumí, nerozumí, a tak proces opakujete? Tak právě --editable zařídí, že se nainstaluje jednou a změny jsou ihned dostupné. Soubory se nezkopírují, ale nalinkují se do pracovního adresáře. Tedy lehčí debugging.

Abych to shrnul, se setup.py je jednodušší správa závislostí, lehčí debugování a mimochodem to také zařídí, že vaše aplikace je importovatelná. Což je o čem Armin tweetoval. No a třeba své důvody brzy sám ještě upřesní. :-)

Kdy je programátor nejefektivnější?

Všiml jsem si na sobě jedné věci, která výrazně ovlivňuje mou efektivitu práce. V menší míře také chuť k práci. Bohužel ji neumím přesně pojmenovat, je to spíš mix různých věcí, ale věřím, že se mi jednou povede přijít s přesným pojmenováním. Nebo třeba někoho z vás. :-)

Nejpřesněji bych řekl zodpovědnost. Čím větší zodpovědnost mám, tím jsem efektivnější. Zkusím vysvětlit na několika případech.

Pracuji ve volném čase na vlastním projektu, kde řeším vše. Produkťáka, scrummastera, UXáka, front-endového i back-endového programátora, databázového specialistu, systémového administrátora, testera i uživatele. O projektu vím naprosto vše ze všech stran a svůj čas dělím mezi všechny role. Jaký to má dopad na výkon? Za jeden den dokážu naprogramovat tolik věcí, kolik by mi v práci, kde jsem pouze programátor, trvalo několik dní.

Jak je to možné? Jednoduše, při programování na svém projektu mám úplně vše v hlavě. Vše v jedné paměti, žádná latence synchronizace dat, žádné nedorozumění, jednoduše mám všechny možné informace postupovat mílovými kroky kupředu přes všechny překážky. Když v práci narazím na něco, co mi není jasné, potřebji se někoho zeptat. Takové záseky stojí spoustu času, nemluvě o tom, že ta správná osoba s odpovědí nemusí být po ruce. Ještě problemovější je, pokud programátor není seznámen s doménou projektu. Například u obchodní aplikace neznalost fungování obchodního či fakturačního oddělení má velký vliv na efektivitu.

Další příklad: v práci jsem vedl dlouho tým a dokázal jsem udělat neskutečné množství práce. Odmanažerovat, chodit na schůzky, vše sledovat a ještě vyřešit rekordní počet bugů či posunout projekt hodně kupředu. Pár lidí se mne ptalo, jak to dokážu, a já nikdy neměl odpověď. Už zřejmě mám – po pěti letech jsem znal aplikaci téměř celou, naučil jsem se řešenou doménu (obchod a fakturace) a byl jsem team leader, tedy také jsem věděl, co se děje a chystá na celé službě. To mi dalo téměř stejné možnosti jako doma na svém projektu. Při překážce jsem si dokázal odpovědět sám a věděl jsem, kdy si co mohu dovolit.

Vychází mi z toho, že abych byl efektivní, musím splnit následující body:

  • Naučit se doménu. Velmi dobře pochopit proč, jak a co dělám. To mi odstraní záseky, jak mám dané featury naprogramovat.
  • Sledovat co se děje na celé službě/projektu. To mi odstraní záseky při rozhodování, co je efektivní udělat dřív a co později.
  • Být full-stack developer. To mi odstraní záseky pří rozhraní dvou jazyků, typicky server a prohlížeč.
  • Získat důvěru. To mi odstraní záseky při rozhodování, jaké řešení zvolit.

Proto při přechodu do nového týmu nejprve pročtu všechny dostupné dokumentace, jak týmu, tak použitých technologií a knihoven. Prolezu poslední zápisy z retra a plánování. Naučím se zběžně, co se může hodit. Dokončit první úkol na dvě hodiny mi sice trvá třeba týden, ale naboostí mne to na všechny další.

A jak to máte vy, co vám pomáhá programovat efektivněji?

Ruby po vystřízlivění

Ruby mne hodně lákalo. Má několik zajímavých programátorských konceptů a syntaktických vychytávek, které se mi moc líbily a chtěl si je vyzkoušet. Vyzkoušel jsem si tedy Ruby pro jedno webové API a vyhnul se RoR, abych si vyzkoušel skutečné Ruby. Dopadlo to tak, že jsem si API po pár měsících přepsal do oblíbeného Pythonu.

Ruby má totiž opravdu spoustu vychytávek. Problém je, že jich má tolik, až není jasné, kdy bych měl kterou použít. Například se mi líbilo, že lze volat funkci různými způsoby, buď jako normální funkci, nebo jakoby kód funkce byl na místě volání. To pak otevírá možnosti zavolat třeba i předčasné ukončení „rodičovské“ funkce. Jenže pak je každé volání jiné a nikdy nevím, které použít. Je i několik způsobů na dědičnost, vyhazování výjimek, přístupu k atributům, podmínkování, vytváření polí, … a v různých knihovnách se využívají různé způsoby.

Což je problém. Sám jsem nevěděl, jaký přístup zvolit. Víc a víc jsem řešil, jak je která knihovna napsaná a jak bych si měl kód strukturovat. Navíc Ruby má podobný problém jako dřív JavaScript: Ruby on Rails je odpovědí na téměř jakoukoliv otázku. Což mne pak ještě více mátlo, protože jsem těžko rozpoznával, co je vlastně čisté Ruby, co built-in moduly, co third-party moduly a co RoR.

Na Ruby je vlastně krásně vidět problém, který je popisován v knize Coders at Work: Programátoři při psaní kódu chtějí přidat něco svého. Jenže je více věcí, které lze přidat, než věcí, které by se pouze měli přidat.

Zároveň s Ruby jsem na svém projektu použil i Haskell a ten jsem si naopak zamiloval. Je totiž Ruby přesným opakem, základ je velice jednoduchý. Začal jsem si tak více vážit věty z Pythoního modulu this: There should be one– and preferably only one –obvious way to do it.

Takže jsem si Ruby zkusil a tuto kapitolu uzavřel smazáním kódu. Jediné, co mi bude skutečně chybět, jsou vestavěné regulární výrazy.

Komentáře jsou nejdůležitější částí kódu

Už je tomu šest let, co jsem psal o hezkém kódu na Zdrojáku. Sice bych to dnes po zkušenostech na několika velkých projektech napsal lehce jinak, ale stejně je skvělé, jak jsem se sám tenkrát k něčemu takovému dostal a dokázal sdílet s ostatními. Každopádně – popisoval jsem, že komentáře je lepší nepsat. Pokud je nutkání napsat komentář, mnohem lepší varianta je upravit kód, aby byl samopopisný.

S tím stále souhlasím. Dostal jsem se však do bodu, kdy i když je kód popisný a hezký, komentář napíšu. To hned z několika důvodů. Ukážu na příkladech. První příklad budiž snippet z Flasku:

@app.before_request
def require_login():
    if is_user_logged_in():
        return

    if flask.request.endpoint == 'static':
        return

    view = get_current_view()
    if not view:
        return

    if is_view_public():
        return

    flask.abort(403)

Když přečtu za sebou, jak je, hned je zřejmé, co se děje. Volně přeloženo do češtiny: ověř přihlášení před zpracováním jakéhokoliv požadavku. Ale proč se to netýká statických souborů? Statické soubory nejsou chráněné? A vlastně – proč statické soubory řeší Flask a ne něco rychlejšího? Komentáře hned objasní…

@app.before_request
def require_login():
    if is_user_logged_in():
        return

    # Static files are not protected. Only in development without nginx.
    if flask.request.endpoint == 'static':
        return

    # Not found page is not protected.
    view = get_current_view()
    if not view:
        return

    if is_view_public(view):
        return

    flask.abort(403)

Pojďme na další ukázku z jednoho malého scriptu, který jsem nedávno psal:

def load_config_variable(key):
    variable = connection.configserver.variable.getAttributes(key)
    utc = dateutil.tz.tzoffset(None, 0)
    local = dateutil.tz.tzlocal()
    variable['since'] = variable['since'].replace(tzinfo=utc).astimezone(local)
    return variable

Opět je jasné, co se děje. Načítám proměnnou z konfiguračního serveru. Jenže, proč se tu přepočítává datum do jiné časové zóny? A proč zrovna do lokální a ne do jiné? Co se stane, když se změní čas z letního na zimní a obráceně?

#FIXME: Konexe ma spanou implementaci casovych zon. Z aware RPC datetimu prevadi
#       na naive Pythoni datetime (se ztratou informace o zone se zapocitanim
#       offsetu). Takze tu mame naive datum v UTC. Musime ho obohatit tedy o
#       zonu, aby slo do configserveru zpet zapsat.
#       Smazat jakmile se opravi prace se zonou v RPC.

Nyní už není pochyb, proč to tu je. Dokonce i co je potřeba udělat, aby bylo zase v pořádku.

Další ukázka z Reactu:

let EntryActions = {
    (...)
    reportProblem: function(id) {
        AppDispatcher.handleAction({
            actionType: RSSConstants.ENTRY_REPORT_PROBLEM,
            id: id,
        });
        EntryService.reportProblem(id);
    }
    (...)
};

Z úryvku je patrné, že se při odpálení akce z UI vyšle, že je s nějakým záznamem problém. To se pomocí service odešle na server. Ale co když se komunikace se serverem nezdaří? Na to tu nikde není žádný kód, to vypadá na chybu. S komentářem opět dodá informaci o úmyslu:

// Dve moznosti:
//  1) Jen na par tydnu, nez se vyladi zaznamy. Jelikoz se brzy smaze,
//     nema tedy smysl resit rollback v UI.
//  2) Nebo se bude ladit donekonecna, pak ale bude potreba i vyzadovat komentar,
//     aby se dobre zpracovavalo, a tudiz udelat jinak, tedy nema smysl resit rollback v UI.

Další příklad mohou být Jasmínové testy. Kolega napsal vše v pořádku, ale u jednoho describe měl pouze jeden it s prázdným popisem. Zdálo se mi to jako chyba. Přitom to měl promyšlené – struktura testů byla dle logických celků, ale jedna malá drobnost do toho ne úplně zapadala. Ale zachoval to, což bych při návštěvě kódu pravděpodobně změnil. Poprosil jsem ho tedy o komentář, jak zamýšlel strukturu testů, aby další kolemjdoucí dodržel.

Jinými slovy se snažím říct, že vy možná právě teď komentáře u čitelného kódu nepotřebujete. Komentáře s úmyslem jsou ale pro ostatní, včetně vás za pár měsíců či týdnů. Záleží jak dobře paměť slouží. Protože s takovými komentáři se lze vyhnout zbytečným chybám. Líbil se mi na toto téma tweet s následujícím gifem a popiskem „Why is this code here? We don't need this.“. :-)

Dokonce jsem zjistil, že mi pomáhá psát komentáře průběžně při vývoji všeho možného. Často sahám na spoustu míst a měním několik věcí najednou. Prototypuji. Rychle kód prasím. Až pak ho uhlazuji. Přijde mi to rychlejší než sáhodlouze přemýšlet, jak to udělat hezky na první dobrou. Proto si píšu komentáře, co se mi honí hlavou, a při uhlazování je postupně zpracovávám. Ve výsledku commitnu minimum napsaných komentářů, neb je proměnním v samopopisný kód a zůstanou jen ty komentáře vysvětlující úmysl.

A jelikož nejlepší komentář vysvětluje úmysl, přijde mi, že je častokrát důležitější, než kód samotný. Udělal jsem si takový malý průzkum a zjistil, že neexistuje žádný skin, kde by komentář nebyl téměř neviditelný. Proto jsem si sám ve všech používaných editorech nastavil komentáře tučně a křiklavě červené. Mám to tak přes měsíc a naprostá spokojenost.

Už jen škoda míst, kde nelze psát komentáře. Například v debianích souborech bych rád napsal, proč se například do deb balíku dávají i nějaké soubory a jiné naopak ne. Když to pak nejde, musí se napsat do dokumentace bokem. Jenže já mám tak rád, když se dokumentace generuje z kódu a nemusím na to myslet…

Na závěr taková perlička. Před dvěma lety jsem napsal takový ne zrovna hezký (asi tak jako samotný úkol), ale funkční, algoritmus na automatické generování práv v aplikaci. Napsal jsem k pár řádkům následující komentář (na který jsem úspěšně zapomněl) a kluci před pár dny našli:

# So you found a bug here. It's probably 2016. You know, it can't be
# fixed. You have to rewrite it completely. And what about your trip to
# Australia, was it good?

:-)