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?
:-)