Введение
Недавно, на одном из митингов моей команды, была поднята проблема отсутствия документации внутри кода. Было решено назначить ответственного за ведение документации. Нужно было найти подходящие инструменты документирования, определить стиль и регламент, представить решение команде. Я решил, что возьму эту задачу на себя, так как для меня это особенно важный вопрос. Мне, как новому сотруднику, было довольно тяжело разобраться в уже существующем коде без документации, и упустить возможность повлиять на ее появление я просто не мог. В итоге, в процессе определения стандарта для нашей команды и появилась эта статья. Я подумал о том, здесь присутствуют довольно интересные мысли, поэтому решил поделиться ими здесь.
Дисклеймер
Если вы не узнаете из этой статьи ничего нового - я буду только рад. Но я настоятельно рекомендую напомнить своим сотрудникам и коллегам о документировании.
Начало статьи - это мотивационная часть. Если вы достаточно мотивированы писать документацию - можете смело переходить к пункту "Как?". Хотя я рекомендую почитать и "Зачем?", чтобы мотивировать своих коллег на документирование кода было легче.
Так как я С++ разработчик, то и документирование кода я рассматриваю как документирование кода С++. Когда я говорю код, функция, метод или класс, я подразумеваю код, функцию, метод или класс на С++.
Далее я буду употреблять слова "документация" и "комментарии" в одном смысле - документирование кода внутри кода. Хотя, конечно, документацию и комментарии стоит различать.
Зачем?
Здесь я приведу пару пунктов формата вопрос-ответ. Это те вопросы, которые я задавал себе сам и слышал от своих коллег, когда вставал вопрос документирования кода.
"Все и так знают наш код"
Это утверждение справедливо для разработчиков с большим опытом работы в компании, но никак не для тех, кто только включается в проект. Чем больше документации встретит новичок в коде, тем быстрее он сможет включиться в работу. 10 минут чтения документации лучше, чем 2 часа отладки кода."Этот код прост и не нуждается в документации"
Если такая мысль пришла вам в голову, обдумайте ее еще раз. Если подумать еще не помогло, посоветуйтесь со своими коллегами. Если ваш код не вызвал вопросов при одном только взгляде на него, то, возможно, комментирование кода действительно излишне (но это не точно). Этот пункт будет подробнее обсуждаться далее."Этим кодом больше никто пользоваться не будет"
Это неправда. Не использоваться он будет до поры до времени. В моей практике были моменты, когда я реализовывал то, что уже есть в нашей кодовой базе. Отсутствие документации и, соответственно, привычки искать существующие решения в ней, привели меня к дубликации кода."Документация в коде захламляет код"
Да, это безусловно минус. Читать интерфейс класса становится сложней, чрезмерно большое количество комментариев в блоках кода создает шум. Однако, если будут соблюдаться рекомендации, это даст один большой плюс - программисту не нужно будет лезть в отдельное место и искать описание данного метода/класса/структуры/чего угодно. Описание кода находится в самом коде. А чтобы не захламлять интерфейсы класса, документацию к методам можно выносить в файл с реализацией, а в блоках кода установить ограничение на количество строк/слов в комментарии.Я считаю, что в перспективе это гораздо выгодней и удобней, чем не вести документацию или вести ее в отдельном месте. Посмотрите на документацию библиотеки STL (возможно не лучший пример) - в большинстве случаев, мне не нужно открывать cppreference, чтобы прочитать описание методов. В дальнейшем, из документации в коде, можно автоматически создать готовую документацию с помощью генераторов.
Главный ответ на вопрос "Зачем?" - сделать свою жизнь и жизнь своих коллег проще. Избавьтесь от изучения кода там, где этого можно избежать, от "путешествия" по коду, с целью понять, что делает этот код, за что он отвечает. Покажите своим коллегам, что документация - это экономия большого количества времени. Подумайте о новых сотрудниках, которые могут в разы быстрее включиться в проект.
Понятно, что избавиться от изучения кода полностью невозможно: если вы заняты рефакторингом, исправлением багов или оптимизацией - вы в любом случае будете вынуждены изучать код. Но это и не цель документирования. Цель - свести эту потребность к минимуму, предоставить удобные инструменты для быстрого изучения кода и сэкономить время.
Общие советы документирования
Далее я приведу несколько советов, которые я нашел полезными. Если некоторые из них покажутся вам очевидными - замечательно! (вспомните дисклеймер)
Обязательно документируйте то, что кажется вам нетривиальным
Если решение трудно понять при первом его прочтении - решение нуждается в комментариях. Количество документации для такого кода пропорционально зависит от сложности кода.Приведите пример использования вашего кода
Оставьте названия функций/методов/классов, ткните в место, где используется ваш код. Если вы использовали какое-то популярное решение (паттерн), оставьте ссылку на материал по этой теме. Это поможет быстрее сориентироваться в коде. Понятно, что поиск в IDE даст вам ту же информацию, но иногда он бывает долгий, плюс таким образом вы сможете особенно выделить то место, где использование вашего кода продемонстрировано максимально явно.Если решение нетривиально, расскажите о причинах такого решения
Почему использовано именно такое решение? Порой приходится много времени заниматься отладкой и изучением кода, чтобы понять, почему коллега выбрал именно такой подход. Краткий ответ на вопросы зачем и почему сэкономит много времени. Данный пункт переплетается с первым.Постарайтесь найти середину между отсутствием документации и документированием каждой строки
Не нужно писать целую поэму для метода, из названия которого понятно, что он делает (я надеюсь, что название соответствует тому, что он делает). Также не стоит думать, что, например, простой класс не нуждается в подробной документации. Здесь важно найти золотую середину.Не пересказывайте код
Не превращайте ваши комментарии в этоНе забывайте о поддержке документации IDE
Сейчас почти все IDE способны выводить описание класса/метода/функции при взаимодействии с ним (наведение мыши, комбинация клавиш), если описание оформлено в нужном формате. Пишите документацию так, чтобы (1) IDE смогла ее "подхватить", (2) можно было обойтись чтением документации и избежать необходимости открывать код с целью его изучения.Дайте описание поведения вашей функции/метода в случае ошибки
Что вернет ваш код? Возможны ли исключения и какие? Если функция бросает слишком много исключений, возможно не стоит перечислять их все.Помните о том, что ваши комментарии в будущем будут читать другие люди, в том числе и вы
Не пишите документацию ради документации, пишите ее ради облегчения работы своей команды. Задайте себе вопрос: пойму ли я, что делает этот код, через месяц, посмотреть на комментарии? Для более честного ответа, попросите ответить на этот вопрос своих коллег.Не забывайте об актуальности документации
Неактуальная информацияуже неактуальнахуже ее отсутствия.
Вопросы о грамотности, удобстве, понятности документации и прочие популярные я опускаю с надеждой на то, что такие вещи для читателя само собой разумеющееся.
Как?
Далее речь пойдет о стиле документирования и инструментах. Я считаю, что стиль документирования сродни код-стайлу - он также важен и его также нужно регламентировать (чем подробнее и логичнее, тем лучше). Какой стиль выбрать - зависит уже от вас и вашей команды. Этот раздел статьи - одно из видений стиля, который можно использовать.
Документирование можно вести в стандарте JavaDoc. Это удобный формат, который знаком многим IDE и поддерживается Doxygenом. Плюсом для нас было то, что он уже частично используется в нашем проекте, и IDE, в которых мы работаем (CLion, QtCreator, VisualStudio) прямо поддерживают генерацию комментариев в этом стандарте и их отображение.
JavaDoc - довольной гибкий стандарт, который позволяет вести документацию в разных стилях. Вы можете использовать Doxygen для автоматической генерации документации в HTML-формате. Отпадает отсутствие поддержки документации в разных местах.
Далее будут описаны инструменты документации и некоторые правила, которые мы приняли в своей команде.
Многострочные и однострочные комментарии
Doxygen поддерживает много видов комментариев. Для нашей команды я выделил такие:
/**
* ... Многострочный комментарий ...
* ... вторая строчка многострочного комментария ...
*/
/// Однострочный комментарий
Или
/* Еще один однострочный комментарий */
Хочу обратить внимание, что
/// ... первая строка ...
/// ... вторая строка ...
И
/**
... первая строка ...
... вторая строка ...
*/
Дадут один и тот же результат. Мы придерживаемся такой стилистики: комментарий из одной строки - однострочный комментарий, комментарий из двух и более строк - многострочный комментарий.
Также существует такой вид комментария:
///< это комментарий к переменной
Такой вариант удобен, когда нужно задокументировать одну строку внутри функции, или дать описание переменной или члену класса/метода. Обратите внимание, что между слэшем и стрелкой нет пробела.
Комментарии такого вида я выбрал потому, что они показались мне наиболее аккуратными. В комментариях в стиле Qt меня раздражал не к месту добавленный пробел, а в однострочных комментариях - обилие слешей. Плюс в тех IDE, в которых работаем мы, комментарии такого вида генерируются автоматически.
Команды
В JavaDoc есть так называемые команды. Команды служат для детализации, выделения документации. Команд в JavaDoc довольно много, плюс можно вводить свои. Те команды, которые используем мы, я опишу далее. К сожалению, многие команды (почти все) в C++ не поддерживаются так, как хотелось бы (так, как это сделано в Java). Но тем не менее это отличный способ структурировать вашу документацию, обеспечить удобство при чтении и поддержку Doxygen. Я настоятельно рекомендую пользоваться командами.
Классы, структуры, перечисления, области видимости
Документация должна находиться строго до объявления/описания кода. Это позволит нашим IDE, и в дальнейшем Doxygenу, подхватить комментарии.
Мы документируем такой код многострочным комментарием, даже если описание влезает в одну строку.
/**
* Документация для класса example
*/
class Example
{
...
};
Это позволяет комментариям не выбиваться из общего стиля документирования. Комментарии в одну строку блочного кода должны быть редкостью!
Для указания краткого описания может быть использована команда @brief. Указанный после команды текст, вплоть до конца параграфа, будет относиться к краткому описанию, и для отделения от основного используется пустая строка.
/**
* @brief Краткое описание и
* его продолжение.
*
* Подробное описание
*/
Насколько такая команда полезна - решать вам.
Также можно (и даже нужно) добавлять команду @example. Такой вариант позволит быстрее найти пример использования кода.
/**
* Документация для класса example
* @example path/to/file/file.cpp
*/
class Example
{
...
};
Это отличный вариант соблюдения 2 пункта из советов.
Документация функций/методов
Функции и методы также должны документироваться строго над объявлением/описанием. Здесь я также выступаю за многострочные комментарии по тем же причинам, что и выше.
Пример комментария:
/**
* Документация для функции foo
*/
void foo(int a, size_t b)
{
...
};
int sum(int a, int b)
Здесь я хочу остановится по подробнее на таком вопросе: а нужно ли документировать функцию int sum(int a, int b)
? Документирование функции/метода, по моему мнению, является самой сложной частью документации. В первую очередь, нужно руководствоваться здравым смыслом. Если сигнатура, имена функции и ее аргументов достаточно тривиальны (как в примере выше) - документирование может быть излишне. Но действительно ли все так тривиально? Действительно ли возвращаемое значение и имя функции говорит лучше любого комментария? Подумайте об этом, когда встанет вопрос документации функции/метода.
Для функции будет удобно не только задокументировать описание функции, но и ее сигнатуру - возвращаемый тип, аргументы. Если функция возвращает unordered_map<int, string>
, то возможно стоит объяснить содержание такого ассоциативного массива.
Опишите поведение функции/метода в случае ошибки (что возвращает, какие исключения кидает). Для этого можно использовать команду @throw.
Помните - документация в коде должна избавлять программиста от необходимости переходить к описанию кода.
Для документации сигнатуры используйте @return и @param:
/**
* Это описание функции foo
*
* @param str это описание аргумента str
* @param pattern это описание аргумента pattern
* @return это описание того, что вернет функция
*/
int foo(std::string str, std::string& pattern)
{
...
};
Было бы несправедливо не упомянуть о минусе документирования методов в хедере - захламление интерфейса класса ("Зачем?", пункт 4). К счастью Doxygen, как и CLion, определяет документацию и в cpp файлах. Поэтому описание методов класса можно выносить из хедеров.
Документация членов в классе/структуре, переменных в функции, значений в перечислениях
Для таких объектов будет удобен такой стиль документации:
int result; ///< Краткое описание переменной result
Либо
/// Краткое описание переменной result
int result;
Или на крайний случай
/**
* Длинное описание переменной var,
* которое не поместилось в одну строку
*/
int var;
Документируйте переменные однострочными комментариями, старайтесь уместить комментарий в одну строку. Там, где это невозможно, используйте многострочные комментарии.
Более подробно о технических деталях документации рассказано тут.
Как обеспечить качественную документацию?
Самый очевидный вариант, проверять присутствие документации на ревью. Нет документации - нет MR, все просто. Но как гарантировать нам, что документация действительно получится качественной и понятной? Один из возможных вариантов - доверить ревью документации младшим и средним специалистам.
Если доверить ревью документации вашему опытному коллеге/сотруднику, большая вероятность того, что проверка документации получится некачественной. Опытный программист на то и опытный, что ему не составляет труда разобраться в новом коде. Он прошел через огонь и воду, методы в сотни строк и полную неразбериху в структуре проекта. Для него любая ваша документация - это подарок. Даже если он очень хочет провести качественное ревью, есть риски, что у него это не получится. Что поделать, горе от ума:(
Доверить ревью новичку даст два больших плюса:
Первое - это гарантия качественной документации. Если ваши комментарии понял новичок, его поймет любой другой специалист.
Второе - вы прокачаете своего сотрудника гораздо быстрее. Помимо решений своих задач, сотрудник получает возможность изучать чужой код, ознакамливаться с бОльшими участками проекта.
Понятно, что первое время новичок, возможно, будет стесняться делать замечания своим более опытным коллегам или вообще не понимать что от него требуют. Но если специалист перспективный (заодно и проверите), такое поведение - вопрос времени. Новичку предоставляется возможность поглядеть на код своих более опытных коллег, повзаимодействовать с ними. Это ли не перенимание опыта?
В перспективе вы получите:
Подробную и качественную документацию
Более быстрый рост и включение в проект своих новых сотрудников
Резюме
Понятно, что все, о чем я рассказал выше, "хорошо только на бумаге". Вероятней всего, на практике вы столкнетесь с трудностями: негодование ваших коллег/сотрудников о "бесполезной" работе, "бедной" документации, несогласия о стиле. Но не мне вам говорить, что такие изменения никогда не даются легко:) Грамотный подход к решению вопроса документации в перспективе окупит себя.
Ссылки, которые помогли мне к подготовке данного материала:
https://habr.com/ru/post/252101/ https://softwareengineering.stackexchange.com/questions/246917/how-to-do-documentation-for-code-and-why-is-software-often-poorly-documented