Я присоединился к Aumni после того, как обнаружил, что технический директор, Роб Уайз, был чистым программистом на 10/10, который любил структуру и тесты. Это было дополнительно подкреплено тем, что Роб был вторым самым активным участником Prettier-Atom — инструмента, который почти в одиночку помог мне преодолеть дислексию при обучении программированию. Я чувствовал, что созданная структура и практика дзен помогут мне работать на более высоком уровне эффективности, уверенности и понимания. В целом, это подтверждалось снова и снова.

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

Принцип единой ответственности

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

Модель User — классический пример модели, нарушающей этот принцип. Во многих веб-приложениях модель пользователя становится «божественным объектом», накапливая различные обязанности, связанные с управлением пользователями, аутентификацией, авторизацией, управлением профилями и многим другим. Это нарушение SRP приводит к тесно связанной кодовой базе, ее сложнее поддерживать и она более подвержена ошибкам.

Типичная пользовательская модель может включать методы и логику, связанные с:

  1. Аутентификация пользователя: хранение паролей, хеширование паролей и проверка учетных данных пользователя.
  2. Авторизация пользователя: определение ролей и разрешений пользователя в приложении.
  3. Управление профилями: управление данными профиля пользователя, такими как имена, адреса электронной почты, аватары и настройки.
  4. Управление выставлением счетов и подпиской: управление платежной информацией пользователя, планами подписки и историей платежей.

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

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

class UserAuthentication
  # authentication-related methods
end
class UserProfile
  # profile management-related methods
end
class PasswordReset
  # password reset-related methods
end

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

Согласованные и недвусмысленные соглашения об именах: ключ к ясности и уменьшению количества ошибок

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

Например, если переменная хранит общую стоимость корзины, она должна постоянно называться total_cost во всей кодовой базе и не чередовать такие имена, как cart_total или total_price. Точно так же два разных понятия, такие как общая стоимость корзины и общая стоимость заказа, не должны называться total_cost одновременно. Придерживаясь этих соглашений об именах, разработчики Aumni гарантируют, что их код будет ясным и недвусмысленным, что облегчает понимание и участие всей команды в проекте.

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

  1. Увеличенная когнитивная нагрузка. Разработчики должны помнить несколько названий одной и той же концепции или устранять неоднозначность между похожими именами для разных концепций, что замедляет разработку и затрудняет понимание кода.
  2. Неправильное толкование. Когда два разных понятия имеют одно и то же имя или одно и то же понятие называется по-разному, разработчики могут неправильно понять назначение или функциональность фрагмента кода. Эта путаница может привести к неправильным предположениям, что приведет к ошибкам или ошибочным реализациям.
  3. Проблемы совместной работы. Отсутствие четких и последовательных соглашений об именах может помешать эффективному общению и сотрудничеству в команде. Когда члены команды используют разные имена для одной и той же концепции или одно и то же имя для разных концепций, это может привести к путанице и несоответствию во время обсуждений, проверки кода и сеансов отладки.
  4. Повышенная сложность обслуживания. Несогласованность имен затрудняет поиск и рефакторинг кода, поскольку разработчики должны учитывать несколько вариантов имен. Это может замедлить выполнение задач обслуживания и увеличить вероятность возникновения ошибок при рефакторинге или обновлении кода.

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

Минимизация комментариев

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

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

# Bad example with comments
def process(c_items)
  # Process shopping cart items
  # ...
end
# Good example with self-documenting code
def process_shopping_cart_items(cart_items)
  # ...
end

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

Значимая история коммитов Git

Аумни подчеркивает важность значимой истории коммитов Git, которая служит ценным инструментом документации для понимания эволюции кодовой базы. Коммиты должны быть детализированными и сфокусированными, при этом каждый коммит должен решать конкретную проблему или функцию. Aumni также настаивает на том, чтобы каждый коммит проходил тесты непрерывной интеграции (CI), гарантируя, что кодовая база останется стабильной и функциональной. Эта практика помогает разработчикам отслеживать историю изменений и способствует ответственности и сотрудничеству внутри команды.

Например, вместо того, чтобы делать один коммит с расплывчатым сообщением, таким как «Рефакторинг кода», разработчики Aumni будут создавать несколько небольших коммитов с четкими и описательными сообщениями:

- Refactor User model validations
- Optimize image loading in Product model
- Improve error handling in ShoppingCartController

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

Маленькие методы, вызывающие частные методы

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

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

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

class ShoppingCartProcessor
  def process_shopping_cart(user, cart_items)
    apply_discounts(cart_items)
    total_cost = calculate_total_cost(cart_items)
    send_confirmation_email(user, total_cost)
  end
private  

 def apply_discounts(cart_items)
    # Apply discounts to cart items
 end

 def calculate_total_cost(cart_items)
    # Calculate the total cost of the shopping cart
 end

 def send_confirmation_email(user, total_cost)
    # Send a confirmation email to the user
 end
end

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

Заключение

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

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