Django CSRF bug

We had this problem: our application worked well on desktop but on phone any action had CSRF problem.

Mm, strange. Soon we discovered it's problem only with Android. Later we were sure it's only Chrome on Android. Even stranger!

Next step we did was to check what we are using exactly for CSRF validation. Django. Without customization. Of course, no one had this issue by checking Google, so it had to be some problem with our configuration. But it worked everywhere else…

We found that same cookie was used for our top domain and our application runs on sub-domain. It could be an issue, all browsers can handle it but Chrome on Android has some problem with that, we thought. We changed it. Deployed it. And nothing!

Ok, time to plug the phone and open debug tool to see what's happening. In development environment, everything worked just fine. That's very strange and because of that we were still checking configuration for few more minutes. Without success.

After hour or so we noticed that token was really wrong. Token in response from the server was different than which was then set up to the session. We didn't know what to check next so we did the worst thing possible. Add some logging on the server and see what's happening. We noticed some regeneration of token. Why it's regenerated? And why there is more then one request when using phone?!

And then we finally saw it. Android is doing one request for a page user wants and one for some special 96px version of icon it could be used… somewhere. Which generates 404 page and we have some form at every page. But Django (it took us another half an hour, probably more) doesn't use CSRF middleware for 404 and 500 pages! Which means Django is not going to take token from cookie and when token is needed, new one is generated!

Fix is to use decorator for 404 and 500 views, as described in Django documention. It's there, but who would expect that.

Half of the day. We spent half of the day, two of us, to figure out this issue. I really don't know how to explain to people why development of simple websites cost so much money. Better to just laugh, take a glass of whisky and watch Django Unchained. Much more fun than debug Django app. :-P

Makefile with Python

I like when everything is easy. I like to open some repository and in as few steps as possible to run example and see how it works. Ideally just call run and that's it.

Python is great, Python makes a lot of things very easy. But tooling around, mostly packaging, is not working well. It has a lot of flaws. I'm used to to fix this with Makefile. Some people are furious about that and argue that it's only for building C codes. Well… maybe. But I prefer to be practical.

Check out why I think it's very simple and good to use:

.PHONY: help prepare-dev test lint run doc

VENV_ACTIVATE=. $(VENV_NAME)/bin/activate

.DEFAULT: help
	@echo "make prepare-dev"
	@echo "       prepare development environment, use only once"
	@echo "make test"
	@echo "       run tests"
	@echo "make lint"
	@echo "       run pylint and mypy"
	@echo "make run"
	@echo "       run project"
	@echo "make doc"
	@echo "       build sphinx documentation"

	sudo apt-get -y install python3.5 python3-pip
	python3 -m pip install virtualenv
	make venv

# Requirements are in, so whenever is changed, re-run installation of dependencies.
venv: $(VENV_PATH)/bin/activate
	test -d $(VENV_PATH) || virtualenv -p python3 $(VENV_PATH)
	${PYTHON} -m pip install -U pip
	${PYTHON} -m pip install -e .
	touch $(VENV_PATH)/bin/activate

test: venv
	${PYTHON} -m pytest

lint: venv
	${PYTHON} -m pylint
	${PYTHON} -m mypy

run: venv

doc: venv
	$(VENV_ACTIVATE) && cd docs; make html

It's very simple base Makefile which I like to start with at any project. It helps me to install all dependencies I need without searching and run project (or tests, or lints or whatever) without remembering how the tool for it look like.

The best feature is how Makefile works. Notice venv target. It says that it needs to have script venv/bin/activate which needs Makefile will run venv target only when you remove (or don't have yet) virtual environment or you change Because I use venv as dependency for all tasks, I can change my Python dependencies and just run the tests. Makefile will ensure that virtual environment is updated.

So… maybe Makefile is only for compiling codes… but anyway I think this is very clever. I could use normal bash scripts, sure, but why? It's hard to keep them executable in git repository, not fast to type and I would need to write logic which Makefile already provides.

Not conviced? Well, I don't force you to use it, I'm just saying why I love to use them. :-)

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 update-index --index-info &&
    mv "$" "$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. 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 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 lepší, resp. proč ho Armin doporučuje oproti requirements.txt?

Jak už je zmíněno v článku, 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í, 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 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?