В статье The Life Changing Magic of Tidying Up Code я (Кент Бек) пошагово описал, как приучить себя к повседневной гигиене при создании кода. Код обычно приходит в беспорядок. В этом нет ничего постыдного. Если вам кажется, что код запутался — значит, вы чему-то научились. Чтобы код стал чистым, в нём надо хоть немного прибраться.

В статье о TLCMOTUC описан этот процесс: немного поправить код, остановиться на безопасной стадии, отпраздновать. Ниже я опишу, как именно выглядят некоторые приёмы такой уборки:

Имена


Со временем в коде может меняться тот смысл, который изначально вкладывался в выбранные имена. Команда работает, её словарь развивается. Вчерашние термины сегодня могут восприниматься уже иначе.

Наводите порядок с именами по очереди. Старайтесь, чтобы тому, кто будет читать код после вас, смысл наименования был понятнее, чем вам. Другое наименование поправите в следующий раз.

Условные конструкции


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

Например, условная конструкция скорее во вред, если в ней заключён простой случай. Вот это

if (condition) {
…many lines…
} else {
…one line…
}

переделайте в это:

if (!condition) {
…one line…
} else {
… many lines…
}

(можете считать бонусом, если условная конструкция в коде уже отрицалась, и вам удалось удалить это отрицание.)

В условную конструкцию должен вкладываться смысл: «Здесь вычисление может пойти по двум путям». Но бывает, что программист хочет сказать: «Вот предусловие. Если оно не выполняется, то данное вычисление является недействительным/не является необходимым». Например, если значение кэшировано, то вычислять его не нужно.

if (cache) {
return cache;
} else {
…compute the cached value…
return cache;
}

В этом фрагменте кода предлагаются альтернативы, которые на самом деле не равноценны. Здесь идёт предусловие, а затем вычисление. Можно сделать чище, переоформив это в виде охранного выражения:

if (cache) return cache;
…compute cached value…
return cache;

Следующий читатель вас поблагодарит.

Наконец, иногда программист имеет в виду «значение можно вычислить одним из двух способов», а не «проследуйте по одному из двух этих путей вычисления». Это тоже подчистим:

if (condition) {
temp := …something…
} else {
temp := …something else…
}

Воспользуемся тернарным оператором:

temp := condition
? …something…
: …something else…

Избыточность


По мере того, как код развивается, в нём накапливается всё больше решений, принятых с целью локальной оптимизации – в том числе, там, где можно было бы выразиться короче и яснее. Почистите такие места по одному за раз. Замените “if (flag == true)” на “if (flag)” (если, конечно, в вашем языке программирования так можно). Замените “collection.size == 0” на “collection.isEmpty”.

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

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

Извлечение


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

Подчищайте выражения, выделяя из них значимые части, при этом давайте временной переменной такое название, которое подсказывает её смысл. Из этого

return new Point(…длинное выражение, чтобы вычислить x…, …длинное выражение, чтобы вычислить y, который интересен нам уже по другим причинам, чем x…)

сделайте это:

x := выражение для вычисления x;
y := выражение для вычисления y;
return new Point(x, y);

При чистке кода совершенно нормально выделить из кода одну объясняющую переменную и сделать коммит. Остаток выражения успеете подчистить позже.

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

Более амбициозная (я бы сказал, «отважная») операция — извлечь метод объекта. Методы объектов часто открывают путь к радикальной дальнейшей уборке в коде, но при плановой уборке просто извлечь новый объект и вызвать его — уже немалая работа для одного коммита.

Переупорядочивание


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

input() {
…stuff…
}

process() {
…stuff…
}

output() {
…stuff…
}

main() {
input()
process()
output()
}

или:

main() {
input()
process()
output()
}

input() {
…stuff…
}

process() {
…stuff…
}

output() {
…stuff…
}

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

Достаточно


Я сам удивлён, что написал это эссе. Всегда сам говорю: «от сверки к сверке не сильно подчищайте код, будет время и для следующей маленькой уборки». Такая установка, что времени у нас много, и оно обязательно найдётся¸ прямо противоречит тому, что я здесь написал. «Давайте почистим код прямо сейчас, ведь следующего шанса может уже не быть».

Предлагаю вам присоединиться к этому внутреннему диалогу. Как бы вы действовали, если бы знали, что времени у вас достаточно? Может быть, дополнительная энергия, креативность и аккуратность помогут вам успеть больше, а не меньше. Как это будет круто, представляете?

Но такая уборка в программе не отменяет необходимости добавлять в неё новую логику. Если вы знаете, что хотите написать — пишите. Почистите позже.

Время для этого найдётся.

p/s две книги Кента Бека: Чистый дизайн. Практика эмпирического проектирования ПО и Экстремальное программирование: разработка через тестирование

Комментарии (1)


  1. danilovmy
    23.06.2025 13:13

    Этой статье около 8 лет (опубл. Sep 7, 2018). Вы машину времени изобрели?