Clean Code

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

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

С чем связано такое неприятие комментариев? 

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

“Комментарии — это не список Шиндлера. Не стоит относиться к ним, как к “абсолютному добру”.На самом деле комментарии в лучшем случае являются неизбежным злом”,— Роберт С. Мартин, “Чистый код: создание, анализ и рефакторинг. Библиотека программиста”.  

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

Хорошие комментарии 

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

Юридические тонкости 

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

Волшебные выражения 

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

// Регулярные выражения для сопоставления адресов e-mail 
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());

// Обратите внимание, что при добавлении описательного имени функции, у вас может отпасть необходимость в этом комментарии. 

function validateEmail(email) { 
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(String(email).toLowerCase());
}

Объяснение намерения 

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

for x in range(1, 500):
  
  # Это наша лучшая попытка предотвратить взаимную блокировку  
  # с помощью нескольких параллельных тестов. 
  time.sleep(0.5)
  runTest(x)

Предупреждение о последствиях 

Уместен, более того, даже приветствуется комментарий, поясняющий радикальные или неприятные последствия. В данном примере программист объясняет, что функции QT не является потокобезопасными при совместном использовании с обратным вызовом. В целом, если комментарий помогает программисту не впасть в бездну отчаяния, то он несомненно полезен. 

"""
  Многие функции QT не являются потокобезопасными. Если вы используете обратный вызов функции, то даже при условии установки блокировки на все ВАШИ вызовы отрисовки, произойдет аварийное завершение программы, так как главный цикл обработки события QT все еще выполняется и использует ресурсы без блокировки.
"""

from multiprocessing.pool import ThreadPool
import sys
from threading import Lock
import time

from PyQt5 import QtCore, QtWidgets

class Task(QtCore.QObject):
    updated = QtCore.pyqtSignal(int, int)
    ...............
    ...............

Комментарии TODO 

Эти комментарии помогают указывать на действия, которые должны быть выполнены, но пока отсутствуют условия для их осуществления. Они могут служить напоминанием об удалении устаревшей функции или требующемся изменении в зависимости от планируемого события. Комментарий может принять форму просьбы с пожеланием обратить внимание на какую-то проблему или придумать более подходящее имя.  

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

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

Плохие комментарии 

Список плохих комментариев гораздо длиннее, поэтому в этом разделе мы остановимся только на самых типичных и распространенных из них. 

Очевидные комментарии 

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

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

/**
* Кластер, с которым ассоциируется данный Container. */
protected Cluster cluster = null;

/**
* Легко читаемое имя данного Container. */
protected String name = null;

/**
* Родительский Container, которому данный Container приходится потомком. */
protected Container parent = null;
/**

* Родительский класс Loader, который необходимо настроить при установке * Loader.
*/
protected ClassLoader parentClassLoader = null;

Бормотание 

У вас не получится сформулировать толковые комментарии, если они были обусловлены политикой компании или потребностью написать хоть что-нибудь. Поэтому если вы вынуждены внести пояснение, то сделайте его как можно более информативным. 

def load_config():
  try:
    do_useful_stuff()
  except Exception as ex: 
    # Не сработало. Возврат к параметрам по умолчанию. 

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

Закомментированный код 

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

Шумные комментарии 

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

-----------------------------
# Демонстрация A 

# Конструктор по умолчанию 
def get_todays_date():
  return date.today()

-----------------------------
# Демонстрация B 

# Возвращает день месяца 
# @return: день месяца 
def get_day_of_month()
  return day_of_month

Поборите в себе искушение “пошуметь” и замените его намерением написать чистый код, тогда станете и чуть профессиональнее, и чуть счастливее. 

Вынужденные комментарии 

Ситуация с этим видом комментариев является спорной. Вас никогда не смущало правило о том, что каждой функции нужны Java doc или Python docstring? В большинстве случаев они избыточны по отношению к тому, о чем нам уже говорит имя класса или функции. В этом примере больше комментариев, чем самого кода, так что в глазах рябит: 

class ComplexNumber: 
    """ 
    Данный класс предназначен для математических операций с комплексными числами. 
      
    Атрибуты: 
        real (int): действительная часть комплексного числа. 
        imag (int): мнимая часть комплексного числа. 
    """
  
    def __init__(self, real, imag): 
        """ 
       Конструктор для класса ComplexNumber.  
  
        Параметры: 
           real (int): действительная часть комплексного числа.
           imag (int): мнимая часть комплексного числа.
        """
  
    def add(self, num): 
        """ 
       Функция для добавления двух комплексных чисел. 
  
        Параметры: 
            num (ComplexNumber): комплексное число для добавления. 
          
        Возвращение: 
            ComplexNumber: комплексное число, содержащее сумму. 
        """
  
        re = self.real + num.real 
        im = self.imag + num.imag 
  
        return ComplexNumber(re, im) 
  
help(ComplexNumber)  # для обращения к строке документации класса 
help(ComplexNumber.add)  # для обращения к строке документации метода 

Использование хороших имен функций или переменных 

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

# Проверка, относится ли дата к прошлому 
def check_date(date):
   if date < today:
     return true 
   return false 


def is_past_date(date):
   if date < today:
     return true 
   return false

Комментарии не компенсируют плохой код 

Одним из самых распространенных поводов написать комментарий является плохой код. Все из нас не только встречали подобные примеры, но и сами становились их авторами. Мы прописывали модуль или класс, а в душе знали, что порождаем хаос. Тогда возникала мысль: “О, это надо прокомментировать!”. Нет! Это надо почистить!

/* 
Что за отвратительный код! Я знаю это, вы знаете это, все знают это. Но мы сделаем вид, что ничего не произошло и продолжим, а назвать меня идиотом вы еще успеете. 
*/

Краткие выводы 

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

Поэтому давайте придем к обоюдному соглашению и перестанем писать так много комментариев. 

Читайте также:


Перевод статьи Tameem Iftikhar: Every time you comment code — you’ve already failed

Предыдущая статьяСтоит ли заменить Python на Julia?
Следующая статьяНесколько распространенных вопросов об ElasticSearch