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

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

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

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

Код был прост. Настройте стандартный рабочий процесс Sidekiq для запуска каждую ночь и удалите строки, которые мы классифицировали как «истекшие» (старше 3 месяцев).

Запрос Active Record для выполнения этого был таким же простым, как

Price.expired.delete_all

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

Price.expired.count == 0

Price.order(:created_at).limit(1)

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

Все начало становиться странным, когда я рискнул выйти за пределы среды Rails. Я вошел в клиент MySQL (в котором на тот момент я был довольно новичком). У нас нет локальной базы данных для этого проекта, поэтому мы клонируем из промежуточной стадии, если хотим заполнить нашу локальную среду. После получения переменных среды для базы данных, включая хост, имя пользователя и пароль, и клонирования их через клиент MySQL, я запустил обработчик базы данных и понял, что на самом деле он не работает.

Я проверил консоль Rails. Rails говорит мне, что это работает. В настоящее время в этой таблице нет данных старше 3 месяцев, согласно моему запросу ActiveRecord. Клиент MySQL говорит мне обратное. Может ли это быть проблемой окружающей среды? Правильно ли я настроил свою локальную базу данных на этом клиенте, которым я только начал учиться пользоваться? Я как-то неправильно сформировал этот запрос Active Record? ВТФ происходит?

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

default_scope { where(current: true)}

Я никогда раньше не видел и не слышал об этом в Active Record. Объем по умолчанию? Значит, вы говорите, что для каждого запроса, который я запускаю, предложение WHERE добавляется без моего ведома?

Как это было обнаружено? Один из моих коллег сказал, что я должен посмотреть на запросы к базе данных, выполняемые Active Record и SQL, и убедиться, что они одинаковы. Я читал об этом давным-давно, но забыл, насколько полезным может быть простой вызов .to_sql в запросе Active Record. Это приведет к выводу необработанного SQL, который будет выполняться на уровне базы данных. Видя, что дополнительное предложение WHERE привело нас к самой модели, где была обнаружена неприятная область действия по умолчанию.

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

Еще один пример удивительного поведения, который я не буду подробно рассматривать, — это использование constantize в Ruby. Этот метод по существу преобразует строку в объявленную константу. Итак, если у вас есть класс с именем GetGasPrices где-то в кодовой базе, и вы вызываете "GetGasPrices”.constantize.new, то у вас есть экземпляр класса GetGasPrices. Это становится ужасным, когда у вас есть код, динамически генерирующий набор похожих классов.

Скажем, у вас есть GetGasPrices, GetElectricityPrices и GetInsurancePrices каждый в качестве класса уровня обслуживания. Вы хотите вызвать всех троих. контроллер, но в этот момент у вас есть доступ только к типу продукта («газ», «электричество» и «страхование»). Если вы хотите генерировать их динамически, вы можете сделать что-то вроде

"Get#{product_type.capitalize}Prices”.constantize

Это был еще один сюрприз, который я обнаружил в последнее время. Просматривая код и пытаясь понять больше о службе GetGasPrices, я должен был понять, как она используется. Простой глобальный поиск по кодовой базе не дал результатов для этого класса. Это просто мертвый код? Могу ли я удалить эту услугу? Звонящего нет, кого это волнует??

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

Это лишь два примера, с которыми я столкнулся недавно, которые застали меня врасплох и заставили подумать: «Ах, это непослушно!». Вывод из этого следующий: всякий раз, когда вы пишете код, всегда напоминайте себе, что многие люди будут читать его в будущем и не всегда будут иметь такое же контекстуальное понимание, как у вас сейчас. Сделайте одолжение вашим будущим сопровождающим и постарайтесь не удивить их.