от Лиор Бар

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

Чистота - королева

Впервые я по-настоящему ощутил силу чистого кода на мероприятии компании Lightricks. Мы были командой из трех человек, участвовавших в алгоритмическом соревновании Google, и временные рамки составляли очень сжатые четыре часа. Пока мы готовились к конкурсу, Михаил «Кими» Кимьягаров настоял, чтобы мы следовали принципам чистого кода, а не просто взламывали как можно быстрее, несмотря на сжатые сроки.

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

Запутанная реальность

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

Небольшие функции

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

Автор Чистого кода Роберт Дядя Боб Мартин рекомендует разбивать код на мини-функции, каждая из которых состоит всего из нескольких строк. Он предлагает максимум 20, но приводит пример программы только с четырьмя строками на функцию. Разрешение всего нескольких строк кода для каждой функции может привести к коду равиоли, в котором так много маленьких функций или классов, что становится трудно следовать. Как читатель кода, вы можете обнаружить, что DFSируете функцию за функцией, достигая точки, в которой вы настолько глубоко, что забываете, с чего начали и что искали.

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

Интерфейсы

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

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

Комментарии

Убежденные чистые кодеры будут идти против комментариев в коде. Мартин пишет, что «что» должно быть ясно из кода, а «почему» должно быть написано в сообщениях коммитов.

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

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

Форматирование

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

СУХОЙ — не повторяйтесь

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

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

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

imagesView(DataSource dataSource, Layout layout)

Но мы не хотим повторяться:

Layout layout = TileLayout();

Итак, мы хотим расширить абстракцию до imagesTileView и imagesGridView.

Но как насчет типа клетки? Если у нас есть два типа ячеек, теперь нам нужны четыре функции. И поэтому абстракция продолжает расти в объеме и сложности. Вот два подхода, которые я нашел полезными:

  1. Если абстракция имеет смысл в проблемной области, это означает, что у нас меньше шансов иметь кучу разных вариантов. Варианты, которые у нас есть, являются частью проблемной области, поэтому сложность, которую они добавляют (например, больше функций, о которых должен знать программист), имеет смысл в контексте. Мы стремимся абстрагироваться, чтобы поддерживать то, что нам нужно в ближайшем будущем.
  2. Часто изменения заключаются в том, что мы называем шаблонным кодом, который соединяет элементы воедино, например:
Layout layout = TileLayout();
CellView cellView = CellView();

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

Программируйте с открытыми глазами

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