Komentáře - deep dive
Abych omezil rozsah článku, zaměřím se pouze na komentáře pro kód, který je používaný malým počtem programátorů - kód, který typicky používá pouze váš (relativně malý) tým nebo jen velice omezená skupina lidí.
Pokud píšete kód, který používá velké množství programátorů (package, veřejné API, knihovny, frameworky, obří monolitické aplikace atd.), tak se potřebujete řídit jinými pravidly a tento článek není pro vás.
Hlavní problém komentářů
Pravděpodobně jste slyšeli o třech hlavních problémech komentářů:
- Programátoři nečtou komentáře, protože často nic neříkají.
- Komentářům se nedá věřit, protože je lidé zapomínají aktualizovat.
- Programátoři používají komentáře místo toho, aby napsali lepší kód.
Tyto obecné rady představují důležitý základ, ale reálné použití komentářů a jejich efektivita je mnohem komplexnější téma.
Dva druhy komentářů
Komentáře můžeme rozdělit na 2 druhy:
- API comments - vysvětlují, jak má programátor metodu (třídu atd.) použít.
- Details comments - popisují detaily metody (třídy atd.).
Následující příklad loggeru (.NET Serilog) ukazuje, jak vypadají oba druhy komentářů:
/// <summary>
/// Write a log event with the specified level.
/// </summary>
/// <param name="level">The level of the event.</param>
/// <param name="messageTemplate">Message template describing the event.</param>
public void Write(LogEventLevel level, string messageTemplate)
{
// Avoid the array allocation and any boxing allocations when the level isn't enabled
if (IsEnabled(level))
{
Write(level, messageTemplate, NoPropertyValues);
}
}
První komentář:
/// <summary>
/// Write a log event with the specified level.
/// </summary>
/// <param name="level">The level of the event.</param>
/// <param name="messageTemplate">Message template describing the event.</param>
je příklad API komentáře. Druhy komentář:
// Avoid the array allocation and any boxing allocations when the level isn't enabled
je příklad detailního komentáře - je to informace, která je určena pro někoho, kdo se snaží hlouběji pochopit detaily metody. Pokud chcete pouze zavolat Write
, tak se pravděpodobně bez této informace obejdete.
Viditelnost Api komentářů a detailních komentářů
API komentářů si programátor často nevšimne
Důvodů je několik:
- Programátor píše moc rychle a IDE nestihne komentář zobrazit.
- Programátor zkopíruje celé volání a IDE nemá ani možnost komentář zobrazit.
- Programátor je líný číst.
V praxi se pak typicky stává, že programátor API komentář čte, až když se zasekne na nějakém problému.
Detailní komentáře programátor vždy vidí
Programátor čte detailní komentáře, až když potřebuje porozumět detailům metody, například při ladění nebo refaktoringu. Jelikož kvůli tomu otevírá definici metody, má tyto komentáře přímo před sebou
To, že programátor vidí komentář přímo před sebou, neznamená, že ho přečte.
Jak donutit programátory, aby četli detailní komentáře
Jelikož programátor detailní komentář vidí, tak ho stačí přesvědčit, že ho má přečíst. K tomu můžeme použít několik triků:
- Do interních komentářů pište jen opravdu důležité informace.
- Zaveďte týmové pravidlo - “pokud vidíš detailní komentář, tak ho přečti”.
- Nastavte barvu komentářů na rudou v celém týmu nebo použijte better comments - Visual Studio Code, Rider, Visual Studio
Kdy nepoužívat API komentáře
Nepoužívejte je pro nestabilní API
Knihovny si mohou dovolit používat API komentáře pro jejich public API, jelikož mají jistotu, že se nebude moc často měnit. Pokud API změní, tak musejí vydat novou verzi, což je poměrně velký zásah, kterému se chtějí vyhnout.
Váš kód se oproti API knihoven pravděpodobně mění poměrně často - je tedy důležité používat API komentáře pouze pokud víte, že se API nebude moc často měnit.
Příklady API ve vašem kódu, které se často nemění a jsou vhodné pro API komentáře:
- Infrastruktura - scheduler tasků, používání front, cache, key-value store atd.
- Volání externích endpointů - služby, které voláte, obvykle nedělají mnoho breaking změn.
- Konfigurace - obvykle pouze přidáváte další položky.
Nepopisujte proměnlivé informace
Snažte se vyhýbat popisu informací, které se mohou změnit – typicky požadavky zákazníka. Naopak je dobré popisovat informace, které jsou obecně pravdivé.
Například řekněme, že máme metodu, pomocí které vkládá administrátor nové produkty:
public static void AddProduct(string productName)
Někoho by mohlo napadnout: „Co se stane, když přidáme dvakrát stejný produkt?“ Je to validní otázka a možná by stálo za to napsat komentář. Je ale důležité, jak přesně komentář napíšeme. Pokud napíšeme:
///<remarks>
/// In real world it can happen that there are two products with the same name
/// therefore we allow multiple products with the same name.
///</remarks>
public static void AddProduct(string productName)
je to ok, jelikož popisuje obecnou skutečnost, která se pravděpodobně nikdy nezmění. Pokud bychom ale napsali:
///<remarks>
/// In real world it can happen that there are two products with the same name
/// therefore we allow multiple products with the same name.
///
/// When user adds product with already existing name we ask for confirmation.
///</remarks>
public static void AddProduct(string productName)
Věta When user adds product with already existing name we ask for confirmation.
je nebezpečná, jelikož je to něco, co se může změnit, a tento komentář pravděpodobně zapomeneme aktualizovat.
Nepoužívejte je pro doménové informace, které jsou zřejmé
Software, který píšete, pravděpodobně popisuje nějakou doménu - obchod, banku, přehrávač hudby, vytápění atd. Vaši programátoři se s touto doménou seznámí po několika týdnech či měsících práce na projektu a proto nemá obvykle význam psát API komentáře, které vyplývají z dané domény.
Příklad:
/// <summary>
/// Starts process which tries to sell more products to the user.
/// </summary>
public void StartUpselling()
{
}
Tento komentář je zbytečný, jelikož by všichni programátoři pracující v doméně e-commerce měli vědět, co je to “upselling”.
Nepoužívejte je pro předání důležitých informací
Vždy když chcete napsat API komentář, tak si uvědomte následující: “programátor API komentář čte, až když se zasekne na nějakém problému”.
Například následující použití API komentáře je velice nešťastný:
/// <remarks>
/// !!!IMPORTANT!! Authors must be in alphabetical order!!!.
/// </remarks>
public void SaveSong(Guid songId, Author[] authors)
{
}
Lepší řešení jsou:
- Metoda
SaveSong
může autory interně seřadit před tím, než s nimi bude dále pracovat. - Místo předání
Author[]
je možné vytvořit vlastní datový typAuthorsInAlphabeticalOrder
a ten použít jako parametr metodySaveSong(Guid songId, AuthorsInAlphabeticalOrder authors)
. - Přejmenovat parametr
authors
naauthorsInAlphabeticalOrder
- zvýšíte šanci, že si programátor informaci přečte.
Nepoužívejte je jako náhradu dlouhých názvů
Někteří lidé nemají rádi dlouhé názvy a snaží se je nahradit API komentáři. Dlouhé názvy jsou lepší než komentáře, jelikož máte jistotu, že si je programátor přečte.
Například místo tohoto:
/// <remarks>
/// If user already exists we update his data
/// </remark>
public void Add(User user)
{
}
je lepší napsat:
public void AddOrUpdate(User user)
{
}
Kdy nepsat detailní komentáře
Nenahrazujte volání metod a použití proměnných komentáři
Namísto:
private void ProcessUrl()
{
var url = GetUrl();
// Remove https:// from url
if (url.StartsWith("https://"))
{
url = url.Substring("https://".Length);
}
// ... some more code
}
napište:
private void ProcessUrl()
{
var url = GetUrl();
url = RemovePrefixIfPresent(url, "https://");
// ... some more code
}
private string RemovePrefixIfPresent(string str, string prefix)
{
if (str.StartsWith(prefix))
{
str = str.Substring(prefix.Length);
}
return str;
}
Obecná pravidla pro API komentáře i detailní komentáře
Vždy si rozmyslete, zda opravdu chcete napsat komentář
- Každý zbytečný komentář, zvyšuje pravděpodobnost, že programátor přestane komentáře číst úplně.
- Každý napsaný komentář zvyšuje pravděpodobnost, že ho zapomenete aktualizovat.
Pište komentáře tak, aby byly vidět při úpravě kódu
Je dobré, když komentáře popisují jen tu část kódu, u které jsou umístěny. Když budete později tento kód měnit, snáze si takového komentáře všіmnete a zaktualizujete ho, aby odpovídal změnám.
Například tento komentář je špatný:
/// <remarks>
/// Liked songs are saved to user profile and used to improve AI recommendations.
/// </remarks>
public void LikeSong();
Pokud by se kód doporučovacího algoritmu změnil tak, že by přestal zohledňovat oblíbené písničky, je pravděpodobné, že tento komentář nebude aktualizován, jelikož se nachází v úplně jiné části kódu než doporučovací algoritmus.
Nepište příliš málo komentářů
Psaní komentářů je obtížné umění. Mnoho programátorů jich už z principu píše jen minimum. Obvykle to jsou ti, kteří se řídí radou Uncle Boba - “The proper use of comments is to compensate for our failure to express ourself in code. Comments are always failures.”
Pokud budete přistupovat k psaní komentářů pragmaticky a vyhýbat se problémům popsaným v tomto článku, tak byste měli dojít k udržitelným komentářům, které vám víc pomáhají než škodí.
Jeden dobrý komentář může ušetřit hodiny práce.
Základní pravidla pro psaní komentářů
Pro úplnost uvedu ještě základní pravidla pro psaní komentářů, která pravděpodobně znáte.
Kdy psát komentáře
Zde je neúplný seznam situací, kdy je téměř vždy vhodné napsat komentář(e):
- Kód dělá něco neočekávaného - něco, co není na první pohled zřejmé.
- Optimalizovaný kód - optimalizace jsou často neintuitivní na pochopení a komentář může hodně pomoci.
- Složitý kód - matematické algoritmy, složité podmínky atd.
- Pokud jste zkoušeli několik variant implementace, které nefungovaly, tak je obvykle vhodné zdokumentovat, co jste zkoušeli a proč to nefungovalo.
Neduplikujte kód v komentáři

Snažte se v komentáři popsat proč, a ne co
Například namísto:
// Resets the order
order.reset();
dává větší smysl napsat:
// We reset the order here so it doesn't interfere with the next order
order.reset();
Referencujte jiné weby z komentářů
- Nebojte se referencovat issues, kvůli kterým jste museli udělat neintuitivní změny v kódu.
- Nebojte se referencovat externí weby, pokud implementujete nějaký obecně známý algoritmus, např. můžete přidat odkaz na stránku, která vysvětluje, jak algoritmus funguje.
Například:
// See visualization here: https://en.wikipedia.org/wiki/File:Bubble-sort-example-300px.gif
// For more information on bubble sort, see: https://en.wikipedia.org/wiki/Bubble_sort
public int[] BubbleSort(int[] numbers)
{
// ..
}
Používejte ukázky
Jeden z nejlepších způsobů, jak vysvětlit, co kód dělá, je ukázka. Pokud máte složitý kód, tak je skvělé napsat nějaký příklad.
Pokud bysme pro předchozí příklad bublesortu nemohli najít žádný odkaz tak můžeme napsat vlastní příklad:
// Bubble sort compares two neighboring elements and swaps them if they are in the wrong order.
// For example, if the input array is [5, 3, 8, 4], the first comparison will be between 5 and 3.
// If 5 is greater than 3, they will be swapped, resulting in [3, 5, 8, 4].
// Then 5 is compared with 8 and so on.
// Example of sorting for [5, 3, 8, 4] (each step shows one comparison):
// First pass:
// [3, 5, 8, 4] -> [3, 5, 8, 4] -> [3, 5, 4, 8]
// Second pass
// [3, 5, 4, 8] -> [3, 4, 5, 8] -> [3, 4, 5, 8]
// Third pass
// [3, 4, 5, 8] -> [3, 4, 5, 8] -> [3, 4, 5, 8]
// Algorithm ends when no swaps are made in a pass.
public int[] BubbleSort(int[] numbers)
{
// ..
}