Эта история изначально появилась вHacker Noon 10 февраля 2017 г.

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

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

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

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

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

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

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

Комментарии

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

public class Class1
{
  public List<string> DoWork(List<string> a)
  {
    List<string> numbers = new List<string>();
    
    // Loop over data
    for (int i = 1; i < a.Count; i++)
    {
      int s = a[i].IndexOf(" ");
      string num = a[i].Substring(0,s);
      
      // Save data
      numbers.Add(num);
    }
    
    return numbers;
  }
  ...
}

«Зацикливание данных»? "Сохранять данные"? Эти комментарии полезны для понимания кода. Я могу легко сказать, что у меня есть цикл, и что я добавляю свои данные в коллекцию, почему я должен тратить ценное пространство на экране на бесполезные комментарии?

Вместо того, чтобы говорить что или как, комментарии должны объяснять почему. Программист увидит цикл for и узнает, что он зацикливается на каком-то наборе Addresses. Однако программист не будет знать, почему мы начинаем наш счетчик с int i = 1 — именно здесь добавление комментария может улучшить понимание кода:

// i = 1 because the view will never display the first address
for (int i = 1; i < Addresses.Count; i++) {...

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

Кроме того, мы полностью удаляем комментарий // Save data, так как он не добавляет никакой полезной информации.

Самодокументация

Однако сами по себе комментарии не облегчат переинтерпретацию кода. Давайте снова вернемся к нашему методу с нашими улучшенными комментариями:

public class Class1
{
  public List<string> DoWork(List<string> a)
  {
    List<string> numbers = new List<string>();
    
    // i = 1 because the view will never display the first address
    for (int i = 1; i < a.Count; i++)
    {
      int s = a[i].IndexOf(" ");
      string num = a[i].Substring(0,s);
    
      numbers.Add(num);
    }
    
    return numbers;
  }
  ...
}

Что такое Class1? Какую работу выполняетDoWork()? Как насчет использования int s? Имена объектов в нашем коде не помогают нам понять, что делает этот код.

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

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

А как насчет имени метода List<string> DoWork(List<string> a)? Что ж, я могу сказать вам, что этот метод пытается разобрать числовую часть почтового адреса. Итак, давайте изменим имя метода и сигнатуру на что-то более информативное, например List<string> ParseHouseNumbers(List<String> addresses). Теперь мы можем сделать обоснованное предположение, что этот метод принимает некоторые строки адресов в качестве входных данных и возвращает список проанализированных номеров домов.

Если мы очистим некоторые имена переменных, наш код станет намного легче читать, например:

public class AddressStandardizer
{
  public List<string> DoWork(List<string> addresses)
  {
    List<string> houseNumbers = new List<string>();
    
    // i = 1 because the view will never display the first address
    for (int i = 1; i < addresses.Count; i++)
    {
      int firstSpaceIndex = addresses[i].IndexOf(" ");
      string houseNumber = addresses[i].Substring(0,firstSpaceIndex);
    
      houseNumbers.Add(houseNumber);
    }
    
    return houseNumbers;
  }
  ...
}

Документация

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

Код на данный момент в порядке, но не идеален. Если мы не будем смотреть на этот код в течение нескольких лет, у нас, вероятно, сейчас достаточно информации, чтобы посмотреть на код и относительно легко понять, что он делает.

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

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

Что было бы полезно, так это объяснение того, что делает наша программа на высоком уровне. Это цель официальной документации.

Документация для этого раздела кода может выглядеть примерно так:

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

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

Заворачивать

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

Комментарии в коде должны объяснять, почемуа не как:

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