Здравствуйте, я — разработчик программного обеспечения на Ruby / Rails и я комментирую свой (а с недавних пор ещё и чужой) код. Голос из зала, вероятно, крикнул бы «Привет, разработчик!»
Много лет назад мне казалось очевидным устоявшееся мнение профессионалов и гуру разработки, которое обычно выражается примерно так: «Если код требует комментария — это плохой код, его нужно переписать/отрефакторить/упростить/уменьшить». Т.е. привести его к виду, комментариев и пояснений не требующему. В целом, этот подход достаточно универсален и работает во многих случаях. Многие мои знакомые веб-разработчики никогда не комментируют свой код и считают это вполне нормальным явлением, даже если работают в команде. Вероятно, сказывается миф о простоте Ruby, такой простоте, которая делает код понятным даже постороннему. Однако, мой личный опыт и некоторые эпизоды командной разработки веб-приложений убедили меня в том, что существуют ситуации и причины уделять комментариям и документированию кода больше внимания и времени, чем обычно уделяет разработчик.
Сразу же предупрежу, что в Интернете, вероятно, есть множество статей и постов, излагающих какую-либо точку зрения об искусстве написания кода, о правилах и рекомендациях для каждого конкретного языка программирования. Их все я, конечно же, не могу знать, поэтому экскурса в историю вопроса не будет. Будет только мой личный рассказ о моём личном опыте.
Основным тезисом моего краткого рассказа будет: «О сложности кода может судить только его автор».
Итак, прежде всего нужно уточнить, когда же именно работает правило «Вместо комментирования измени код так, чтобы комментирование стало излишним». Оно работает всего лишь в нескольких случаях:
- Когда код написан неквалифицированно: слишком длинно (возможно, разработчик плохо знает стандартную библиотеку и писал много лишнего кода сам) или слишком сложно (perl-style, однострочные цепочки функций длиной более 5-ти функций подряд)
- Когда код включает в себя редкоиспользуемые или нехарактерные для данного ЯП методы и приёмы
И всё! В этих случаях, действительно, лучше такой код переделать. Переделать, убедится, что код стал короче, яснее, понятнее для коллег и… наконец-то написать комментарии! И вот я уже добрался до того места, где и буду пояснять свои идеи.
Пояснение для Идеи № 1: «О сложности кода адекватно может судить только его автор».
Представим себе команду программистов, состоящую, например, из 5-ти человек. С ними работает менеджер и пара дизайнеров. Команда программистов состоит из одного профессионала и 4-х программистов обычного уровня. Никто не комментирует код. Возможно, есть написанная кем-то краткая инструкция по развёртыванию проекта. Документации команда не ведёт, т.к. заказчик меняет решения и приоритеты и не очень хочет оплачивать время, затрачиваемое на составление документации в процессе разработки. Примерно таким образом работает множество среднего уровня студий веб-разработки.
Что же будет, если один или двое программистов уйдут из команды, в другой проект, в другую студию или просто в отпуск? Вероятно, на их место будут взяты один и два других разработчика, которым дадут код, дадут задачи и… и всё. Предварительно, HR-менеджер спросит у них, умеют ли они «разбираться в чужом коде» и «работать в команде», а они, конечно же, ответят, что умеют. И вновь прибывшие окажутся один на один с кодом и коллегами, которые быть может и рады отвечать на вопросы «а почему здесь так...» или «а зачем там это...», но это отвлекает от работы. Поэтому разработчик вынужден «разбиться с проектом». Количество рабочего времени, потраченного на «выполнение» своим
Представим себе функцию, вроде такой:
def update list = current_user.lists.update(params[:id], params[:list]) end
С точки зрения правила о простоте кода, здесь просто рай. Комментировать тут нечего, одна строка, совершенно никаких сложностей. Однако, представим себе, что params формируется не простой формой на странице, а с помощью кода на Javascript в Backbone.js, что lists — это на самом не lists, а оставшееся от прошлой команды название и модель эта теперь везде в тестах называется Things, что на before_save этой модели навешена функция, которая берёт некие поля и создаёт отложенное задание, которое парсит, согласно данным этих полей некие URL'ы (возможно, без контроля ошибок HTTP-протокола), чтобы потом сохранить полученные ответы в другой таблице. А иногда вызывает exception'ы и шлёт сообщения о них… куда-то… на сайт сбора ошибок, куда программисту дадут доступ, как только он сам напомнит. А совсем иной контроллер (API, например) отдаст значения этих полей ajax'ом в Backbone'овскую View. Кстати, можно ещё уточнить, что данная функция должна рендерить шаблон, сначала пропуская его через парсер RABL, формируя там JSON, совершенно непохожий на изначальное содержимое list, который Things. Ну и для полного комплекта уточним, что работать это должно на
Приведённый пример — это не выдумка, не специальное усложнение. В реальных проектах бывает и больше шагов в выполнении какой-то функции приложения. Но всегда только сам автор кода может знать, сложен его код или нет. Проблема в том, что человек, которому нужно реализовать в этом приложении новую функциональность неизбежно должен будет своим
Есть и другая часто встречающаяся проблема.
В проекте N функциональность M обычно реализуется с помощью XYZ. Не зная кода полностью или не прочитав документации к проекту, описывающей структуру и используемые XZY, разработчик не может знать, X, Y или Z используется ли уже или нет. И если нет — почему не используется. Не существует программиста, который может сразу же начать работать и создавать новое в незнакомом ему проекте, это миф о «крутости разработчика». Лучшее, к чему может привести подобная ситуация — разработчик сделает свою задачу так, как умеет, отдаст на code review и получит код обратно с комментарием «Зачем сделал так, ведь у нас для этого принято использовать [список каких-то технологий]» или «Зачем сделал так, ведь можно было так: [описание сущностей, выяснить существование которых самостоятельно невозможно (например, наличие триггеров или функций в БД на production-сервере]»
Так что же я предлагаю, уже давно пора спросить читателю. Прежде всего, делайте с кодом, над которым работаете, несколько простых вещей:
- Описывайте то, как работает этот код. Обязательно, хоть и кратко, в начале каждого файла, содержащего класс, описывайте, что он реализует. Перед методами класса кратко описывайте, что метод получает и что должен создать в конце своей работы, коротко описывайте цепочку, по которой пройдут данные. Так же в файлах-модулях. Комментируйте вычисляемые автоматически значения и назначения создаваемых миграциями полей. В случае длинной цепочки вызовов функций (длиннее трёх) — описывайте цепочку, что вызывает что, хотя бы просто последовательность.
- Ведите в каждом файле блок TODO, в одном и том же месте, хоть сразу после объявления класса или модуля.
- Ведите в каждом файле блок IN_WORK (назовите как угодно Вам), в котором пишите о том, какие места в данный момент подвергаются переделке/рефакторингу/устарели и вместо них нужно использовать BLABLA из класса XYZ.
- Обязательно описывайте все факты, наличие которых нельзя вывести из имеющейся документации!
Для последнего пункта приведу пример: если ваш проект несовместим, например, с MySQL, а разработчик потратил полдня, пытаясь развернуть проект именно на этой СУБД, не вздумайте сказать ему «Мы же не поддерживаем MySQL!». В ответ вы услышите в лучшем случае «А почему я об этом не знал?». И если вы скажете этому парню фразу «А почему ты не спросил?» — можете быть уверены. что в вашем проекте уже существуют большие проблемы с документированием и это уже наносит вам ущерб. Ведь никто из нас не знает точного списка всего, что он не знает!
Спасибо за внимание и пусть ваш код будет всегда ясен, прост и задокументирован.
Автор: Nimrodel