Pull to refresh

Почему читабельность кода имеет значение?

Reading time 7 min
Views 5.2K
Original author: Ashod Nakashian
Понятно, что напрашивающийся (и правильный) ответ — «Потому что код приходится не только писать, а и читать». Едва ли этот ответ стоит целого поста, но автор им не ограничивается.

Последнее время мои коллеги огрызаются на мои неустанные замечания о стиле в рецензиях на их код. Я уделяю все больше и больше внимания тому, что можно с полным основанием назвать косметикой. Да, есть плохие стили кода, есть лучшие, но все они всего лишь соглашения. Нельзя отрицать, что некоторые из них действительно улучшают общее качество кода, но вне зависимости от того, сколько мы спорим о пробелах и табуляции, они остаются субъективными предпочтениями, часто делом вкуса.

Впрочем, меня беспокоит не качество кода — не настолько, как напряжение зрения и случайные ошибки чтения, которые тоже определяют качество кода. Мы часто бьемся над ошибками, которых можно было бы избежать, если бы не спешка и недопонимание.

Разработчики не только пишут код — большую часть нашего времени мы добавляем, убираем и редактируем небольшие его фрагменты, фактически вносим мелкие изменения в огромный код. Независимые фрагменты кода, написанные с нуля, попадаются редко — если, конечно, мы не начинаем новый проект. Большинство наших проектов — наследие многих поколений разработчиков до нас. Что бы мы ни делали — добавляли новую функциональность или изменяли старую — нам приходится читать код, чтобы выяснить, какая команда что делает и как лучше всего реализовать нашу задумку. Даже при работе над совершенно независимым модулем или классом, как только мы написали первые строки, нам приходится возвращаться к ним снова и снова, чтобы вплетать в них новый код.

Как ни странно, чем опытнее я становлюсь, тем больше времени я уделяю чтению кода перед его изменением, и это кажется мне логичным. Написать новый код гораздо проще, чем понять существующий и использовать его, но самый легкий путь не всегда оказывается лучшим в итоге. Конечно, будет быстрее написать эту функцию сравнения строк без учета регистра в тридцатый раз, когда она снова нам понадобится — или это только нам так кажется? На самом деле существует вероятность того, что в новую функцию вкрадется ошибка-другая, и мы не заметим их, пока не будет слишком поздно. Ненамного сложнее сделать усилие и найти существующую реализацию, которой мы можем воспользоваться — может, даже в нашем старом коде, но для этого нужно уметь его читать. (Я не считаю изобретение велосипедов смертным грехом, бывают случаи, когда это лучший выбор, но это уже совсем другая история.) Другой отличный побочный эффект чтения кода — нахождение ошибок и сообщение о них, если не немедленное исправление.

Разумный выбор между написанием нового кода или использованием имеющегося или библиотечного можно делать только в том случае, когда учтены накладные расходы обоих вариантов — на поиск потенциальных ошибок в первом случае или на изучение и встраивание во втором. К сожалению, осознание этого приходит только с опытом (эффект Даннинга-Крюгера).

Вопрос поддержки кода, полученного в наследство, или написания своего — совсем другой спор; отмечу, что улучшение унаследованного кода почти всегда лучше его уничтожения: это менее рисковано, всегда остаются рабочие фрагменты и есть код, который можно принять за основу. Идея этой статьи в том, что для избегания переписывания кода разработчик должен уметь читать существующий код, и читать его хорошо. А для этого свой код нужно уметь писать так, чтобы он был читабелен.

Перейдем к конкретике. Вот что я считаю важным для улучшения читабельности кода.

1. Согласованность.

Я могу без проблем читать код в практически любом стиле; пришлось научиться адаптироваться. Если я считаю, что мне нужно читать код, приходится соглашаться на то, что его стиль может отличаться от моего любимого. Только к одному приспособиться невозможно — к коду, словно бы написанному пятилетним ребенком, который только что переключился с раскладки QWERTY на Dvorak. Выбираете ли вы стиль сами или его вам навязывают — придерживайтесь его во всем проекте.

2. Согласованность.

Это так важно, что заслуживает двух первых мест в списке. Люди экспериментируют со скобками-в-следующей-строке и скобками-в-этой-строке, потом забывают об этом, и их эксперименты оказываются в репозитории, раздражая остальных членов команды. Серьезно, эксперименты со стилем — это хорошо, но не нужно их коммитить.

3. Добавьте простора.

Код похож на текст: все ненавидят читать «простыни» текста без форматирования и разбивки на абзацы — так вот, к коду это тоже относится. Разделяйте блоки и области видимости пустыми строками; закрывающая скобка — отличное место для них. Хотите добавить комментарий? Отлично, только не нужно газетного стиля, когда код форматируется в две колонки — слева код, справа комментарии. Это бывает полезно, но редко, обычно комментариям самое место перед блоком или командой, а не рядом с ними; перед комментарием лучше тоже добавить пустую строку. Помните, места на диске хватает: разбивайте длинные функции, огромные классы и длинные файлы. Пусть ваши функции будут разумного размера. Используйте метрики наподобие цикломатической сложности для определения момента, когда ваш код становится слишком сложным и, следовательно, сложным для чтения и понимания. Делайте рефакторинг.

4. Отставить баннерные комментарии!

Желание добавить баннер из пяти строк комментария внутри функции — хороший признак необходимости начать новую функцию, перед которой и будет находится этот баннер. Я бы предпочел обойтись вообще без него, но это лучше, чем одна функция на тысячу строк с полудюжиной баннеров. В самом деле, если вы хотите отметить начало нового логического блока, лучше сделать это функцией. То же самое относится к любым длинным функциям, классам и файлам.

5. Не используйте магические числа.

Константы — отличный инструмент для именования чисел, на которых основан код. Значения по умолчанию, размеры контейнеров, таймауты и сообщения пользователям — вот лишь несколько примеров того, что можно вынести в константы. Только не нужно заменять константой любое число — они не для того придуманы.

6. Выбирайте названия с умом.

Существует уйма литературы о именовании переменных и функций и опасностях выбора схожих имен. Тем не менее, некоторые несознательные элементы все еще настаивают на непонятных именах и повторном использовании переменных вне логических границ. Названия должны быть не только осмысленными и недвузначными, но и согласованными во всех функциях, классах и модулях, в идеале во всем проекте. Ну хотя бы попробуйте!

7. Не пишите цепочкой.

Многие практикуют объявления всех переменных одного типа в одной строке через запятую. Это происходит от языков, которые этого требовали и не позволяли объявлять переменные после команд. Если язык позволяет это, объявляйте переменные как можно ближе к месту, где они используются, и не используйте их повторно (разве что в точно таких же целях).
Похожая схема кода цепочкой возникает, когда в качестве аргумента одной функции передают результаты вызова других функций, не сохраняя их в промежуточных переменных. Некоторые считают, что так получается компактнее, и громоздят по 3-4 вложенные функции. Часто получается ровно наоборот, особенно если нужно проверить все побочные эффекты функций в происках ошибки.

8. Ограничьте размеры строк и файлов.

Горизонтальный скроллинг — это ужасно: нельзя ожидать от человека, что он прочитает текст, листая его влево и вправо на каждой строке. У разных людей экраны разного размера, многие используют более крупные шрифты из-за плохого зрения. В то же время многие разработчики используют максимальную ширину своего экрана, а иногда и вовсе не ограничивают длину строки. Это очень неудобно, т.к. во многих средах разработки неудачный построчный перенос кода разрушает его форматирование.
Начать можно с исторического стандарта — 80 символов в строке: не слишком узко и поддерживается при большинстве мониторов и настроек. Если вся команда может работать со строками длиной 100 или 120 символов, можно принять в качестве стандарта их; но здесь важно не перестараться, так как слишком длинные строки плохо читаются сами по себе. Подумайте о газетах и журналах — какие узкие у них колонки и как они читаются. Строки, при чтении которых приходится двигать не только глазами, но и головой, создают дополнительное физическое напряжение и замедляют работу.

9. Делайте области видимости явными.

Во многих языках области видимости неявные. Первая команда после условного ветвления или цикла в языках типа C явно попадает в его область видимости, но не следующие команды. При неудачном форматировании довольно сложно отследить конец блока, чтобы выяснить, какие команды к нему еще принадлежат, а какие — уже нет. Форматирование, выделяющее блоки в явном виде, добавление скобок и пустой строки после закрывающей скобки, даже если тело цикла всего из одной команды, очень улучшает читабельность кода.

10. Комментируйте.

Код, даже запутанный и туманный, прекрасно понимается компьютерами. Людям же, напротив, нужно понимать не только то, что код делает, но и его назначение кода и причины, по которым он был написан именно так. Без понимания определенных конструкций, операций и значений ни один разработчик не может осмысленно поддерживать код. Комментарии — отличный инструмент передачи неочевидных нюансов кода. Объясните бизнес-требования, допущения, требования, заплатки и временные решения. Поговорите с напарником-разработчиком через комментарии. Впрочем, нет правил без исключений — не злоупотребляйте комментариями. Не пишите «сейчас возникнет ошибка» перед генерацией исключения — команда throw говорит сама за себя; лучше объясните, почему в этом случае уже ничего нельзя исправить.

В заключение, отмечу, что некоторый код нужно делать нечитабельным. Да-да, ваши глаза вас не обманывают. Код, который построен на хаках, плохих практиках или временных решениях, должен читаться плохо. Мой лучший пример — приведение типов. Часто экземпляры объектов нужно приводить к определенным типам — это стандартная практика, но не то чтобы рекомендуемая. Запись преобразования в немножко менее читабельном виде — хороший способ заставить читателя остановиться и убедиться, что это не ошибка. Это чем-то похоже на текст, выделенный курсивом, который сложнее читать и который таким образом выделяется. Аналогично, временные решения и хаки должны выделяться. Маркеры TODO и FIXME — хорошее начало; фактически, если где-то в коде и должен быть баннерный комментарий, то именно здесь — пусть известные проблемы выпирают из кода. (Главное — не забыть их исправить! — прим.пер.)

Как и большинство таких статей, эта — преимущественно о принципах читабельности кода, а не конкретных требованиях. Они не жестко заданы и ни в коем случае не полны. Все языки, экосистемы, фреймворки и библиотеки имеют свои нюансы, вещи, явные в одних языках, могут быть неоднозначными и нежелательными в других. Важный вывод, который нужно сделать — это необходимость писать код для читателя, избегать непонятных стилей, следить за контекстом и оценивать, когда нужно написать комментарий, а когда — сделать рефакторинг.
Tags:
Hubs:
+50
Comments 28
Comments Comments 28

Articles