Такое мышление поможет в будущем вам и вашим товарищам по команде.

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

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

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

Пишите чистый, простой и читаемый код

Умный - враг хорошего кода

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

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

В этом упражнении мы должны написать декодер кода Морзе.

Вот код, который я написал:

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

Даже не зная синтаксиса и словаря кода Морзе, вы сможете понять несколько вещей, прочитав мой код:

  1. Слова Морзе разделяются 3 пробелами
  2. Буквы Морзе разделяются 1 пробелом
  3. Есть прямой перевод с букв Морзе на английские буквы.

Вы также должны быть в состоянии выяснить, как я декодировал код Морзе на входе:

  1. Разбейте все предложение на слова;
  2. Разбивать каждое слово на буквы;
  3. Переведите каждую букву на английский

Вышеупомянутые 6 пунктов можно обнаружить, только прочитав код с минимальными усилиями. Можете ли вы сказать то же самое о умном решении? Я не могу. Фактически, я перечитываю этот код после того, как вставил его день назад, и я совершенно забыл, как он работает… (Edit: Если вам интересно, как он работает на самом деле, я попытался объяснить это в одном из комментариев )

Так что же на самом деле помогает нам собрать информацию в первом фрагменте кода?

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

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

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

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

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

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

Пишите полные тесты с понятными названиями

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

В предыдущем декодере кода Морзе есть некоторая информация, которая не была очевидна из самой реализации. Например, есть специальные коды Морзе, такие как знаменитый SOS (··· −−− ···), где буквы не разделяются пробелами. Вы также могли быстро заметить тот факт, что мы игнорировали конечные пробелы. Код отлично справляется с этими краевыми случаями.

Вот вещи, которые можно быстро собрать из модульных тестов для декодера кода Морзе:

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

Мне также нравится GIVEN событие AND некоторое условие THEN это то, что ожидается синтаксисом для именования ваших тестов. Это позволяет просто читать заголовки тестов, чтобы знать стандартный prepare-act-assert, который используется в тестах. Большинство исполнителей тестов JavaScript также должны предоставлять вам результаты тестов в удобном, удобочитаемом виде.

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

Ведите журнал процесса внедрения

Большой! Благодаря чистому коду и отличным тестам у нас теперь должны быть инструменты для понимания примерно 100% того, что делает определенный фрагмент кода, крайних случаев, которые он покрывает, и, возможно, некоторых частей его требований. Но… Хммм… Зачем нам вообще снова понадобился декодер кода Морзе?

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

Здесь очень полезно вести дневник различных решений, которые были приняты (и почему) в ходе выполнения задачи. Чтобы проиллюстрировать идею, я проведу вас через одну из недавних задач, над которыми я работал.

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

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

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

Приняв это первое решение, я приступил к реализации автоматического выключателя. Одним из больших преимуществ использования JavaScript является то, что я смог найти реализацию автоматического выключателя за считанные секунды, что также удовлетворило мои потребности с точки зрения экосистемы (AWS Lambda и DynamoDB).

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

После добавления этого нового модуля в нашу кодовую базу и создания прототипа с его помощью я наткнулся на один из серьезных недостатков использования JavaScript: общедоступные модули очень разнородны. В нашей кодовой базе мы следуем функциональной парадигме, но обнаруженный мной модуль использует объекты. Хотя мы могли бы заставить оба работать вместе, это сделало бы код намного менее понятным. Поэтому я решил создать фасад (подробнее об этом шаблоне проектирования здесь) и инкапсулировать в него модуль. Это позволило нам сэкономить время, используя уже реализованный автоматический выключатель, имея при этом интерфейс, соответствующий нашей парадигме программирования (чистый, простой и читаемый код ;-)), помимо других преимуществ, которые фасад дает нам.

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

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

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

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

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

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

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