Commitujte jako profík!

Commits-1

Commity a jejich popisky (commit messages) jsou často na okraji zájmu vývojářů. Přitom pokud se s nimi pracuje špatně, týmový vývoj často drhne a v průběhu historie projektu se zbytečně ztratí spousta užitečných informací. Pojďme se proto podívat, jak commity tvořit správně.

Ten příběh asi znáte. Jako programátor jste nově přiřazen k projektu, který už nějakou dobu běží. Jako první úkol dostanete provést nějakou drobnou úpravu, do které se nikomu jinému nechce. Ponoříte se do kódu, ale nerozumíte jedné jeho části. Vidíte sice, co kód dělá, ale netušíte proč. Komentář žádný, kolegové krčí rameny, autor už ve firmě nepracuje.

Rozhodnete se tedy podívat do historie těch pár řádek ve verzovacím systému. Najdete commit, který je přidal, a zobrazíte ho. Místo očekávané drobné úpravy ale vyběhne diff přes pět obrazovek. Jeho popisek je lakonické „fixed few bugs“. Chvíli nevěřícně koukáte na obrazovku a pak zalitujete, že jste se místo programování radši nedali na fyziku, když vás ve škole tak bavila.

Commity

Prakticky každý software je dnes vyvíjen ve verzovacím systému. Základem takových systémů jsou commity – změny ve spravovaných souborech. Důležitou součástí commitu je jeho popisek (commit message).

Vytvářet dobré commity a správně je popisovat je – poněkud nečekaně – docela umění. Mnoho vývojářů to nedělá, ať už proto, že si nemyslí, že je to potřeba, nebo protože to neumějí. Výsledkem jsou příběhy jako náš úvodní a spousta ztraceného času a peněz.

Pokud jsou ale commity v projektu vedené dobře, stane se jejich historie časem cenným zdrojem informací nejen pro nováčky v týmu, ale i pro ostřílené vývojáře, kteří jen zapomněli, proč to či ono kdysi dělali. Hodnota těchto informací obvykle velmi rychle roste s časem a změnami v týmu a především u technicky složitých projektů poměrně brzo převýší náklady na jejich pořízení.

Snad ještě důležitější než hodnota historická je u commitů jejich hodnota kolaborační. Většina softwaru vzniká v týmu a commity jsou prostředkem, jak se vývojáři navzájem informují o své práci a můžou ji vzájemně revidovat a kontrolovat. Pokud jsou tvořené správně, ušetří se spousta času na dotazování a vysvětlování.

V pražské pobočce SUSE si času vývojářů ceníme a commity a jejich popisky se tak snažíme tvořit poctivě. Pokud máte rádi Linux a open source, přidejte se k nám.

Jak vypadá dobrý commit?

Základním pravidlem je, že jeden commit by měl řešit právě jeden problém – nikoliv jeho část a nikoliv víc problémů najednou. Commit by měl být atomickou jednotkou – před ním i po něm by kód měl být v konzistentním stavu.

Pokud commit řeší jen jeden problém, je mnohem jednodušší ho pochopit, než když je neúplný nebo řeší víc věcí. Je tak možné se lépe přesvědčit o jeho správnosti a nebo v něm naopak najít chyby. Pokud se nakonec ukáže, že nějaká změna není žádoucí, je také mnohem snazší vrátit zpět jeden specifický commit, než zjišťovat, která část většího commitu je relevantní, případně naopak které všechny commity s vracenou změnou souvisí.

Co je jeden problém?

Definice toho, co je jeden problém, je samozřejmě subjektivní a najít správnou míru chce cvik. Zkušenost říká, že vývojáři mají spíš tendenci tvořit commity příliš velké, než naopak (což je do značné míry dáno i neflexibilitou některých verzovacích systémů, které neumožňují commity snadno dělit). Moje rada tedy zní: pokud si nejste jistit správnou granularitou, přikloňte se spíš k více menším commitům, než naopak.

Co s problémy, které rámec jednoho commitu zjevně přesahují? Správný postup je rozdělit je na podproblémy a každý vyřešit samostatně. Výsledné commity je pak možné propojit např. pomocí prefixu v popisku:

User refactoring 1/5: Make permissions more detailed
User refactoring 2/5: Extract common code to a superclass
.
.
.

Konzistentní stav

Jak jsem již uvedl, před i po commitu by kód měl být v konzistentním stavu. To mimo jiné znamená, že daný kus softwaru se před commitem i po něm chová přesně stejně (krom věcí, které commit cíleně mění) a všechny testy po commitu stále běží. Jinými slovy, nemělo by se stávat, že jeden commit něco rozbije a další to pak spraví. Takový přístup neprospívá třeba continuous integration serverům (které mohou zachytit rozbitý stav a vyvolat planý poplach) a ani lidem (kteří mohou např. revertem commitu nechtěně vrátit kód do rozbitého stavu).

Jedním z důsledků pravidla o konzistentním stavu je, že pokud máte testy, měli byste je měnit spolu s kódem, se kterým souvisí.

Popisky

Důležitou součástí commitu je jeho popisek (commit message). Má dva hlavní cíle:

  1. Popsat provedenou změnu
  2. Vysvětlit, proč bylo potřeba ji provést

S prvním bodem obvykle problémy nejsou, navíc není tak důležitý, protože jen shrnuje informaci zřejmou z kódu. Zato druhý bod je často opomíjen.

Důvod změn v kódu je někdy zjevný (např. přidání nové funkcionality), jindy docela komplikovaný (např. je-li výsledkem dlouhé diskuze zvažující mnoho faktorů nebo několika hodin hledání chyby). Ve chvíli, kdy důvod změny není úplně zřejmý a vy ho nikde nepopíšete, prakticky vyhazujete úsilí, kterým jste k němu dospěli, z okna.

Představte si, že jste strávili dva dny tím, že jste hledali v kódu chybu. Když jste ji našli, přidali jste dva řádky s opravou. Po roce se stane, že někdo úplně jiný bude tuto část kódu kompletně přepisovat. Pokud z komentáře nebo z historie kódu nepochopí, proč tam ony dva řádky jsou, je docela možné, že je vypustí jako nejspíš zbytečné. Výsledek bude, že vaše práce přijde vniveč. Chyba se vrátí a někdo jiný opět stráví dva dny jejím hledáním a opravou. Přitom by stačilo, kdybyste strávili dvě minuty navíc napsáním popisku.

Commit je často součástí práce na vlastnosti či bugu, které jsou evidovány v nějakém externím nástroji (např. issue tracker). V tom případě je dobré v rámci popisku odkázat na příslušnou issue či jinou jednotku, se kterou tento nástroj pracuje. Čtenář tak bude mít přístup ke kontextu, ve kterém byl commit vytvořen. Takový odkaz v mnoha případech může nahradit zdůvodnění (samozřejmě je důležité, aby byl dostatečně stálý a po pár měsících nepřestal platit).

Někdy je naopak dobré napsat zdůvodnění přímo do kódu – jsou totiž případy, kdy bez něj kód nebude srozumitelný a prakticky každý čtenář by si tak musel zdůvodnění pracně dohledávat. Často se jedná o opravy obskurních chyb, workaroundy atd. I zde je dobré odkázat do externích nástrojů, má-li to smysl.

Jak psát dobré popisky?

Při psaní popisku je důležité si uvědomit, pro koho ho vlastně píšete. V první fázi je to především pro vaše kolegy, kteří sledují, co se v projektu děje, případně commity čtou a revidují. Později se jejich čtenáři stanou především vaši kolegové, kteří zjišťují, proč nějaký kus kódu vypadá tak, jak vypadá. Nejlepší způsob, jak napsat dobrý popisek, je zkusit se na chvíli vcítit do těchto rolí a uvědomit si, které informace, které vy teď máte, čtenáři nemají a budou jim chybět.

Z osobní zkušenosti můžu říct, že dobrý bič na psaní užitečných popisků je zavedení povinných vzájemných revizí commitů v rámci týmu. V situaci, kdy každý váš commit musí schválit některý z kolegů, se vám každá nejasnost nebo nepochopení vrátí v podobě dotazů a komentářů. Psaním špatných popisků tak neztrácí čas abstraktní čtenář někdy v budoucnu, ale vy právě teď. To je docela silná motivace.

Formální stránka popisků

Mnohé projekty mají na psaní popisků ke commitům detailní pravidla. Jejich dodržování je podstatné především při přispívání do různých open source projektů, kde obvykle netušíte, jaký workflow a jaké nástroje používají ostatní vývojáři. V interních projektech si často můžete dovolit být volnější.

Před prvním commitem je dobré zjistit, zda daný projekt nějaké takové zásady má, a pokud ano, dodržovat je. Pokud projekt zásady nemá, stojí za to si alespoň projít historii a přizpůsobit se obvyklému stylu.

Popisky v Gitu

Protože Git má mezi verzovacími systémy v současnosti výsadní postavení, zmiňme několik zásad užitečných pro psaní popisků pro něj (volně převzatých z článku Tima Popea).

Git dělí popisky na jednořádkové shrnutí a volitelné detaily, které mohou mít libovolný počet řádků. Obě části jsou oddělené prázdným řádkem. Mnoho nástrojů pracujících s Gitem detaily vynechá, pokud zobrazuje víc commitů najednou. Z toho plyne, že shrnutí je dobré psát tak, aby bylo samostatné a stačilo k pochopení, o čem commit je.

Popisek je nejlépe psát v přítomném čase. Shrnutí by mělo začínat velkým písmenem a nemělo by být ukončeno tečkou, detaily by měly být ve větách jako normální text. Tento formát je kompatibilní s popisky generovanými příkazy jako git merge nebo  git revert.

Délka textu shrnutí by neměla překročit 50–60 znaků, text detailů je nejlépe zalomit na 72 znacích. Obojí je důležité především pro kompatibilitu s různými nástroji a také GitHubem.

Příklady dobrých popisků

Na závěr si ukažme několik popisků z reálných open source projektů, které považuji za dobré a ze kterých si můžete vzít příklad. Pro jejich úplné pochopení je potřeba znát kód a architekturu příslušného projektu, ale i tak je ze všech zřejmé, co commit opravuje a proč. Poslední popisek slouží jako příklad, že jako zdůvodnění někdy stačí odkaz na externí zdroj.

V8

Fix bug in deletion of indexed properties

The delete operator always return true in case of indexed property. It
should return false if an indexed property can't be deleted (eg.
DontDelete attribute is set or a string object is the holder).

Contributed by Peter Varga <pvarga@inf.u-szeged.hu>

BUG=none
TEST=mjsunit/delete-non-configurable

Review URL: https://codereview.chromium.org/11094021
Patch from Peter Varga <pvarga@inf.u-szeged.hu>.

(Reference)

Chromium

Fix wrong truncation of notification popup messages in ash.

views::Label control first calculates the size of the text,
and then tries to render the text in the exact size of rectangle.
If < is used instead of <= in the patched line, the control
flows to the BreakIterator mode, that sometimes computes a
larger width value and causes truncation.

BUG=155663

Review URL: https://codereview.chromium.org/11150013

(Reference)

Google Closure Compiler

Attach types to literals at scope-creation time instead of at
inference time.

Scope-creation already attaches types to function literals at
scope-creation type, so this makes the other literals more consistent
with function literals.

R=johnlenz
DELTA=167  (102 added, 53 deleted, 12 changed)

Revision created by MOE tool push_codebase.
MOE_MIGRATION=209649

(Reference)

Rails

Move two hotspots to use Hash[] rather than Hash#dup

https://bugs.ruby-lang.org/issues/7166

(Reference)

Závěr

Práce s commity a jejich popisky je důležitější, než se na první pohled zdá. Pokud vám článek v tomto směru nedal nic nového, je to dobře – s commity nejspíš už pracujete správně. A pokud jste se naopak něco dověděli, zbývá jen nabyté znalosti aplikovat v praxi a hlavně předat všem spolupracovníkům, kteří commitují změny přes pět obrazovek popisem fixed few bugs

Autor je vývojář se zájmem o programovací jazyky, webové aplikace a problémy programování jako takového. Vystudoval informatiku na MFF UK a během studií zde i trochu učil. Aktuálně pracuje v SUSE.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 54

Přehled komentářů

Michal Re: Commitujte jako profík!
Honza Re: Commitujte jako profík!
j Re: Commitujte jako profík!
arron Re: Commitujte jako profík!
5o Re: Commitujte jako profík!
lenken Re: Commitujte jako profík!
Martin Hassman Re: Commitujte jako profík!
nepodstatné Re: Commitujte jako profík!
Franta Re: Commitujte jako profík!
vbl Re: Commitujte jako profík!
Cermi Popisek důvodů v commitu?
jos Re: Popisek důvodů v commitu?
Honza Re: Popisek důvodů v commitu?
Martin Re: Popisek důvodů v commitu?
mchf Re: Popisek důvodů v commitu?
j Re: Popisek důvodů v commitu?
Jerry12 Re: Popisek důvodů v commitu?
vks Re: Popisek důvodů v commitu?
jos Re: Popisek důvodů v commitu?
Honza Re: Popisek důvodů v commitu?
jos Re: Popisek důvodů v commitu?
ijacek Re: Popisek důvodů v commitu?
jos Re: Popisek důvodů v commitu?
disposable Re: Popisek důvodů v commitu?
arron Re: Popisek důvodů v commitu?
http://pavlix.net/ Re: Popisek důvodů v commitu?
arron Re: Popisek důvodů v commitu?
Pavel Šimerda Re: Popisek důvodů v commitu?
Karel Re: Popisek důvodů v commitu?
sachy Re: Popisek důvodů v commitu?
Nox Re: Popisek důvodů v commitu?
dejfson Re: Popisek důvodů v commitu?
Sten Re: Popisek důvodů v commitu?
ijacek Re: Popisek důvodů v commitu?
David Majda Re: Popisek důvodů v commitu?
http://pavlix.net/ Re: Popisek důvodů v commitu?
nobody Commit
arron Preemptive commit comments
arron Komentář kódu v commitu?
Čelo Commit(-message) Driven Development
Petr Blaho chybka ve jméně
David Majda Re: chybka ve jméně
Ondrej Mikle git bisect/hg bisect; high-level docs
jviki Re: Commitujte jako profík!
Diskobolos Jako se vším
lobo komentare
Mé oblíbené Re: komentare
Herbert Re: komentare
Me Pletení
Netvorus Svata pravda
Jakub Vrána Otestování
David Majda Re: Otestování
Jakub Vrána Re: Otestování
David Majda Re: Otestování
Zdroj: https://www.zdrojak.cz/?p=3734