Komentáře v CSS kódu: kdy pomáhají a kdy škodí
Na počátku každého komentáře bývá ušlechtilý úmysl: pomoci těm, kdo přijdou po nás. Dobře postavený komentář skutečně může významně pomoci. Přesto je většina komentářů v kódu zbytečná, odvádí pozornost a v krajních případech může mít i devastující účinky. Jak můžeme kód místo komentování vylepšit?
Na počátku každého komentáře bývá ušlechtilý úmysl: pomoci těm, kdo přijdou po nás. Dobře postavený komentář skutečně může významně pomoci. Přesto je většina komentářů v kódu zbytečná, odvádí pozornost a v krajních případech může mít i devastující účinky. Na příkladech z praxe si jednotlivé situace rozebereme a navrhneme možná lepší řešení.
Softwarový projekt můžeme dokumentovat řadou způsobů: v manuálu (dokumentaci), v README, v komentářích, ale také v Gitu, pull requestech nebo issue trackeru. Samozřejmě také vlastním kódem. Pro každou situaci se bude hodit jiná možnost a hledisek pro vyhodnocení a volbu nejvhodnější formy je více; budeme se rozhodovat, zda potřebujeme popisem doplnit jeden, či více souborů. Zda dokomentujeme jen momentální cestu vývoje, nebo má mít náš text delší časovou platnost. Zda potřebujeme popsat řešení implementace, nebo naopak sepsat návod či postup, které nelze spojit s konkrétním kódem. Záleží také na tom, jestli cílíme na kolegy vývojáře, nebo na méně zasvěcené uživatele a spolupracovníky v netechnických rolích.
Možností dokumentace je celá řada a umět sáhnout po té správné vyžaduje zkušenosti. Dnes se podíváme na nejen dokumentační funkci komentářů v CSS kódu a na to, kdy jsou komentáře v CSS k užitku a kdy naopak ke škodě.
Kód místo komentáře
Říká se, že dobrý kód je samodokumentující. V dobrém kódu by nás nemělo nic překvapit. Dobrý kód by měl být čitelný a srozumitelný, měli bychom v něm věci nacházet tam, kde je očekáváme. V dobrém kódu by tím pádem komentáře neměly být potřeba.
Skutečnost však není tak jednoduchá, a to z několika důvodů. První v řadě je zkušenost vývojáře — psát opravdu dobrý kód, který nepotřebuje komentáře, vyžaduje velkou znalost jazyka a obecně nemalé zkušenosti. Za druhé, záleží na povaze a možnostech jazyka — v některých jazycích některé konstrukce nikdy nebudou snadno čitelné a komentářům se tedy nevyhneme. Za třetí, otázkou je i očekávaná úroveň znalostí případného čtenáře kódu, neboli cílové skupiny — pro koho komentář píšeme?
V CSS jsou klasickým kandidátem na takové zlepšení výpočty:
// Width is 100% minus border minus padding
width: calc(100% - 2 * 1px - 2 * 15px);
Komentáře se snadno zbavíme šikovnými proměnnými:
width: calc(100% - 2 * $border-width - 2 * $padding-horizontal);
Komentář jako forma dokumentace
Čistě samodokumentující kód je však obtížně dosažitelný ideál. I proto si při vývoji často pomáháme psaním komentářů. Existuje jich přinejmenším několik druhů:
- Popis implementace
- Strukturální komentáře
- Zbytečné komentáře
- Zakomentovaný kód
- TODO komentáře
Podívejme se na jednotlivé kategorie blíže.
Dobré komentáře
Popis implementace
Začněme pozitivně. V životě vývojáře jsou situace, kdy se napsání komentáře do kódu nevyhne. Vlastní kód se v několika kolech podařilo vylepšit, přes veškerou snahu mu však stále chybí něco, co umožní budoucím čtenářům smysl oněch řádků pochopit v rozumně krátkém čase. To může nastat v případě, že náš kód je ovlivněný něčím zvenčí, co předurčuje způsob řešení, nebo jednoduše proto, že expresivní možnosti daného jazyka byly vyčerpány. V CSS poměrně častá věc.
Přeskočme triviální příklady a mějme následující SCSS komponentu:
.phone {
position: relative;
max-width: 20rem;
margin-right: auto;
margin-left: auto;
transform: scale(1.05, 1.05);
transform-origin: center top;
&::after {
content: '';
display: block;
padding-top: 694px / 320px * 100%;
}
}
.phone__image {
position: relative;
top: -2.5%;
left: -30.5%;
width: 135%;
height: 115.275%;
}
.phone__frame__screen-area {
position: absolute;
top: 6.4%;
left: 24%;
z-index: 0;
width: 71%;
height: 79%;
overflow: hidden;
background-color: map-get($colors-grays, black);
}
Kód je od pohledu zahlcený složitým pozicováním, bulharskými konstantami, nejasnými kombinacemi jednotek a výpočty. A teď tu zkuste něco upravit bez použití metody pokus–omyl. Je sice dobrým zvykem, že čas strávený čtením kódu výrazně převyšuje čas strávený jeho psaním, málokomu se ale bude chtít pustit do náročné detektivní práce namísto smysluplné tvůrčí činnosti.
Co kdybychom ale od počátku měli představu o tom, co daná komponenta dělá a jakým způsobem dosahuje kýženého chování? Co kdyby nám autor zanechal dokumentaci a usnadnil tak budoucí úpravy komponenty?
// Responsive phone mock-up with interchangeable screen content.
//
// 1. Phone screen content (image or video) is sized responsively
// according to screen area (2) dimensions.
// 2. Screen area is sized relatively to the phone frame with shadow (3).
// 3. Phone frame image, including outer shadows, sits on top of the
// screen area.
// 4. Resulting composition is sized and placed inside phone canvas.
// Edges of the phone are aligned to the canvas edges so the whole
// mock-up can be sized and placed with focus on the area of actual
// device. Outer shadows (actual overflowing image) are not taken
// into account and they behave like a real box shadow.
// 5. To be truly responsive without having to specify any exact
// dimensions, the `padding-top` trick is used to keep aspect ratio
// of the mock-up.
// 6. On small screens, dimensions of the mock-up are limited. The
// mock-up is scaled a bit to exceed the container and the it is
// centered horizontally on the page.
.phone {
position: relative; // 5.
max-width: 20rem; // 6.
margin-right: auto; // 6.
margin-left: auto; // 6.
transform: scale(1.05, 1.05); // 6.
transform-origin: center top; // 6.
&::after {
content: ''; // 5.
display: block; // 5.
padding-top: 694px / 320px * 100%; // 5.
}
}
.phone__image {
position: relative; // 4.
top: -2.5%; // 4.
left: -30.5%; // 4.
width: 135%; // 4.
height: 115.275%; // 4.
}
.phone__frame__screen-area {
position: absolute; // 2.
top: 6.4%; // 2.
left: 24%; // 2.
z-index: 0; // 3.
width: 71%; // 2.
height: 79%; // 2.
overflow: hidden;
background-color: map-get($colors-grays, black);
}
Kód by bylo jistě možné dále vylepšovat: pojmenovat číselné hodnoty přes SCSS proměnné, kdykoli je to možné; ještě lépe ilustrovat vnitřní kompozici komponenty, anebo zapracovat na angličtině komentáře.
Již toto řešení — a to je ta slíbená pozitivní zpráva — nám však zásadně usnadňuje práci, protože:
- Máme představu o tom, jak komponenta funguje uvnitř. Při pozdějších úpravách víme, kam (přibližně) sáhnout.
- Každý řádek má svůj důvod. Díky dokumentaci víme jaký.
- Vidíme, jak jsou spolu provázaná jednotlivá CSS pravidla. Pozicování v CSS často vede k rozmístění úzce souvisejících pravidel napříč celým CSS souborem, takže jejich účel může být jen při pohledu na okolní řádky kódu nejasný. Forma číslovaných referencí je unikátním řešením na míru tomuto specifiku CSS.
- Můžeme se implementaci divit, můžeme s ní nesouhlasit, ale díky dokumentaci alespoň víme, jak je navržena.
Kód v ukázce je inspirovaný skutečným příkladem z reálného projektu, který je k dispozici na GitHubu.
Špatné komentáře
Strukturální komentáře
Známe to všichni. Kódujeme nový projekt, sbírka komponent se utěšeně rozrůstá, souborů a preprocesorových proměnných přibývá. Některé soubory jsou už tak dlouhé, že je těžké se v nich vyznat. A tady přichází ke slovu starý zvyk, zakořeněný v našich návycích ještě z doby předpreprocesorové, kdy stylopis celého projektu dřímal v jediném souboru — strukturální komentáře:
/*
=======================================================================
Typography
=======================================================================
*/
/*
=======================================================================
Animations
=======================================================================
*/
/*
=======================================================================
General elements
=======================================================================
*/
Nejtěžší bývalo rozhodnout, jak budou komentáře formátovány. První úroveň musela být patřičně impozantní, aby byla vidět při rychlém projíždění souborem. Za ní následovaly úrovně další:
/*
=======================================================================
Header
=======================================================================
*/
/*** Logo & brand ***/
/* Logo and website heading */
/* Website heading */
/* Hover state */
Skoro jako v Markdownu… Až nástup preprocesorů nás naučil, že co je dlouhé, nepotřebuje opatřit komentáři, nýbrž rozdělit do souborů. Místo jednoho souboru o mnoha tisících řádků začalo být snadné mít souborů libovolně mnoho a natolik malých, abychom si pohledem na jeden takový soubor udělali rychlou představu o tom, co dělá. Malé soubory zároveň vedou k psaní malých komponent s velkou přenositelností.
Jak by mohly vypadat ukázky výše po provedení řezů v místech strukturálních komentářů?
@import 'typography';
@import 'animations';
@import 'general';
@import 'components/header';
@import 'components/footer';
A i když můžeme mít někdy pocit, že absence strukturálních komentářů uškodí přehlednosti kódu, je skoro jisté, že jsou to obavy liché:
//
// Font families
// =============
$typography-font-family-sans-serif: 'Libre Franklin', helvetica, arial, sans-serif;
$typography-font-family-serif: 'Libre Baskerville', georgia, times, serif;
//
// Font weight values
// ==================
$typography-font-weight-values: (
thin: 100,
light: 300,
regular: 400,
bold: 700,
black: 900,
);
//
// Font size scale
// ===============
$typography-font-size-base: 100%;
$typography-font-size-base-x2l: 106.25%;
$typography-font-size-base-x3l: 112.5%;
$typography-font-size-base-x4l: 118.75%;
$typography-line-height-base: 1.75rem;
Po odstranění komentářů:
$typography-font-family-sans-serif: 'Libre Franklin', helvetica, arial, sans-serif;
$typography-font-family-serif: 'Libre Baskerville', georgia, times, serif;
$typography-font-weight-values: (
thin: 100,
light: 300,
regular: 400,
bold: 700,
black: 900,
);
$typography-font-size-base: 100%;
$typography-font-size-base-x2l: 106.25%;
$typography-font-size-base-x3l: 112.5%;
$typography-font-size-base-x4l: 118.75%;
$typography-line-height-base: 1.75rem;
Výsledek je kratší a přestože to bude otázka osobního vkusu, sotva méně přehledný.
Nakonec je dobré vědět, že dělení do souborů se nemusíme bát ani v druhé a další úrovni. Místo jednoho obřího souboru s konfigurací typografie_typography.scss
je mnohem lepší mít více menších souborů, které se díky tomu lépe spravují:
@import 'typography';
@import 'typography-styles-base';
@import 'typography-styles-components';
@import 'typography-styles-shared';
Zbytečné komentáře
Strukturální komentáře nás občas vedou ke komentování těch částí kódu, které komentář nevyžadují, ale tak nějak ze setrvačnosti si nedokážeme pomoci a komentářem je přesto doplníme:
/*** Thumbnails ***/
/* Thumbnail container */
.thumbnail {
padding: 9px;
border-radius: 0;
background-color: white;
}
/* Image */
.thumbnail img {
margin-bottom: .75em;
}
Takové komentáře ale nepřináší žádnou přidanou hodnotu, protože vše je zjevné přímo z kódu. Můžeme je proto s klidným svědomím smazat a výslednému kódu bude rozumět stejně dobře:
.thumbnail {
padding: 9px;
border-radius: 0;
background-color: white;
}
.thumbnail img {
margin-bottom: .75em;
}
Co je zjevné, není třeba komentovat.
Zakomentovaný kód
Zatímco strukturální a zbytečné komentáře vedou k tomu, že část zdrojového kódu čteme zbytečně, protože takové komentáře nepřináší žádnou hodnotu, existuje ještě jedna, mnohem horší situace: zbytečná práce. Práce, která nemá žádný smysl a do které bychom se nebyli pouštěli, pokud bychom bývali věděli, že není k užitku.
Zakomentovaný kód je vysoce škodlivým způsobem, jak snadno a v dobré víře někomu přidělat hodně práce. V našem přehledu stojí trochu bokem, protože účelem zakomentování kódu není ani tak dokumentace, jako odložení nepotřebného kódu (trochu) stranou pro teoretické budoucí účely.
Nevinnou odrůdou tohoto neduhu je zakomentování pár řádků nebo bloku kódu:
.icon-box__icon {
height: 3rem;
// margin-top: 1rem;
// margin-bottom: 1rem;
}
Docela jinačím žertem je zakomentování volání souboru a jeho ponechání v repozitáři:
@import 'components/button';
// @import 'components/collapsible';
// @import 'components/figure';
@import 'components/footer';
@import 'components/header';
// @import 'components/interlude';
@import 'components/main';
// @import 'components/icon-box';
To je přinejmenším velice neodpovědné, protože při nejbližším větším refactoringu bude vývojář nevyhnutelně ztrácet čas úpravami kódu v zakomentovaných souborech, který se reálně nikde nepoužívá, protože v samotných souborech není poznat, že nejsou nikde volány.
Vývojáři často namítají, že zakomentovaný kód se může v budoucnu hodit, že bude snadné jej pak pouze odkomentovat, až k potřebě dojde. Realita je ale taková, že tato budoucnost, přiznejme si na rovinu, zpravidla nikdy nenastane. Náprava je přitom snadná — nebát se využívat možností verzovacího systému a mazat. Kdyby na budoucí potřebu nyní neaktivního kódu přece jen došlo, v historii verzovacího systému lze cokoli dohledat. Není to tak snadné jako odkomentovat pár řádků, v součtu je to ale mnohem jednodušší než několik měsíců či let neustále zakopávat o kód, o kterém v lepším případě víme, v horším nevíme, že není nikde použitý.
TODO: sporné komentáře
Oblíbenou kategorií jsou TODO komentáře. Ty vznikají, když při práci s kódem máme v danou chvíli pocit, že by něco mělo být řešeno jinak, že jsme něčím blokováni, pro nedostatek času volíme nečisté řešení, které bychom měli brzy refaktorovat, či se jen potřebujeme podělit o své aktuální pocity z kódu.
TODO komentáře jsou dobré i špatné. Některé nám neřeknou zhola nic:
/* TODO */
h2:not(:first-child) {
margin-top: var(--rui-typography-line-height-base);
}
Je mnohem lepší, pokud víme, co se vlastně má stát:
/* TODO to be moved to RUI. */
h2:not(:first-child) {
margin-top: var(--rui-typography-line-height-base);
}
Ještě lepší je, když víme, kdo za komentářem stojí a kdy se má úprava stát:
/* TODO Adam 13/11/2019: To be moved to RUI during issue #89. */
h2:not(:first-child) {
margin-top: var(--rui-typography-line-height-base);
}
A tím se dostáváme k jádru problému: pokud TODO při vývoji systematicky a pravidelně neprocházíme, úkol zadaný jako komentář do kódu se pravděpodobně nikdy nestane, protože nedojde k jeho naplánování. Každý tým používá pro plánování práce nějaký software, a proto je vhodné úkoly udržovat právě a pouze v něm, i když jsou třeba technické povahy. V opačném případě riskujeme, že TODO nebude nikdy vyřešeno, protože na něj nebude vyhrazen čas.
V komentáři je záměrně i jméno autora a datum přidání. To je informace, která ve verzovacím systému sice je, ale nemusí být dostupná na první pohled, pokud řádky s TODO komentáři postupem času prošly rukama více vývojářů.
Shrnutí
Psaní komentářů (a dokumentace obecně) má přinejmenším jednu ohromnou výhodu: bystří mysl a nutí nás k zamyšlení, zda to, co píšeme, opravdu dává smysl. Přemýšlení o právě psaném komentáři dává vývojářům jedinečnou příležitost ke kritickému pohledu na vlastní práci, možnost vcítit se do role těch, co přijdou po nás.
Je dobré mít na paměti, že preprocesory posunuly zkušenost z psaní CSS směrem k programovacím jazykům. Díky preprocesorům dokážeme být při stylování expresivnější a šikovným využitím proměnných, funkcí, mixinů i struktury souborů dovedeme eliminovat potřebu komentářů jen na ty nejnutnější případy. A i v takových situacích se můžeme zamyslet nad tím, jestli je komentář tím správným řešením, které přinese budoucím kolegům hodnotu, nebo místo toho raději sáhneme po jiném, spolehlivějším a udržitelnějším postupu.
A jaké jsou vaše zkušenosti s komentáři v kódu? O komentářích prosím směle komentujte níže v komentářích.
Další články od autora:
Sdílejte
Líbí se vám článek? Podpořte jej sdílením!
Komentujte
Chcete k článku něco doplnit? Našli jste chybu? Napište e-mail.