На этой неделе у меня состоялся разговор с одним из наших инженеров о «дерьмовом коде», в результате которого я решил поделиться с ним одним из своих необычных вдохновений: Flamework, псевдо-фреймворк, созданный в Flickr.
Две страсти, два подхода
В моей работе есть две движущие страсти. Одна из них — любовь к созданию красивого, элегантного кода — создание библиотек и API с открытым исходным кодом, ориентированных на ясный дизайн и возможность повторного использования. Другая страсть — создание быстрых, прагматичных решений для реальных пользователей (которые могут даже не быть разработчиками). Последнее обычно происходит в условиях создания продукта, когда продукт — это не код. Здесь скорость и итерации имеют большее значение, чем красивый код или возможность повторного использования, потому что успех зависит от поставки чего-то, что нужно людям.
Flamework служит последнему и грубо нарушает первое.
С самого начала я понял, что создание многократно используемого кода и непосредственное решение проблем для пользователей часто противоречат друг другу. Первая подсказка пришла ко мне, когда я помогал вести немецкий сайт ubuntuusers. Он работал на сильно модифицированной версии phpBB, которая, несмотря на то, что была грязной, при правильном исправлении масштабировалась до большой базы пользователей. Код был грязным, но легко настраивался. Абстракции были глубиной в один слой.
Тогда мы с другом пытались заменить его, написав собственную программу для досок объявлений, Pocoo. Работа в изоляции, без пользователей, привела меня на путь чрезмерной инженерии. Хотя мы многому научились и в итоге создали популярные библиотеки с открытым исходным кодом (такие как Jinja, Werkzeug и Pygments), Pocoo так и не стал полноценным продуктом. Позже я и мои соавторы переделали ubuntuusers, не ставя перед собой цель превратить его в продукт многократного использования. Эта переделка была успешно завершена, и она живет по сей день.
Но мне потребовались годы, чтобы полностью осознать, почему так происходит: возможность повторного использования не так важна, когда вы создаете приложение, но она крайне важна, когда вы создаете библиотеку или фреймворк.
Философия Flickr
Если вы не знакомы с Flamework, вам стоит посмотреть доклад, который Кэл Хендерсон сделал в 2008 году на DjangoCon (“Почему я ненавижу Django”). Он говорил о масштабировании и о том, что Django не предназначен для этого. Он перечислил все важные для него вещи: шардинг, использование пользовательских последовательностей для первичных ключей, отказ от джойнов и внешних ключей, поддержка настроек репликации базы данных, денормализация данных до крайности. Здесь же я впервые узнал о возможности передачи всех сессионных данных в cookies с помощью подписи. Для меня это было запоминающееся выступление, потому что оно показало мне, что есть недостатки. Django (который я использовал для ubuntuusers) имел прекрасные API, но в то время решал лишь малую часть того, что было нужно Кэлу. Этот доклад действительно запомнился мне.
На момент выступления Flamework еще не существовал. Это была скорее идея и принципы работы инженеров во Flickr.
Несколько лет спустя Flamework появился на GitHub, но не как открытый кусок кода Flickr, а как реализация тех же идей. Вы можете изучить его репозиторий и увидеть код, подобный этому:
function _db_update($tbl, $hash, $where, $cluster, $shard){ $bits = array(); foreach(array_keys($hash) as $k){ $bits[] = "`$k`='$hash[$k]'"; } return _db_write("UPDATE $tbl SET ".implode(', ',$bits)." WHERE $where", $cluster, $shard); }
Инстинктивно это вгоняет меня в кринж. Это что, SQL-инъекция? Ну, вы должны были предварительно использовать функцию PHP addslashes. Но обратите внимание, как она учитывает шардинг и кластеризацию непосредственно в функции запроса.
Беспорядочный, но эффективный
Подобный код часто вызывает бурную реакцию, особенно у инженеров, которые ценят чистый дизайн.
Как создается нечто подобное? Кэл Хендерсон описал принцип работы Flickr так: «Делать как можно более тупую вещь, которая будет работать». Возможно, слово «тупой» слишком сильно сказано — «простой» было бы более уместно. Однако простота может показаться грязной тому, кто ожидает тщательно продуманной кодовой базы. Это совсем не редкость, и я видел это снова и снова. Первый крупный коммерческий проект, получивший распространение, над которым я работал (Plurk), тоже был довольно прагматичным и грязным внутри. Мой бывший коллега Бен Винегар также недавно поделился историей о раннем, грязном коде FreshBooks и о том, как он с этим смирился. В Sentry была та же история. Мы двигались быстро, и у нас был беспорядок.
В ретроспективе все это не удивительно. Идеальный код не гарантирует успеха, если вы не решили реальную проблему для реальных людей. Стремление к элегантности в вакууме приводит к заброшенным побочным проектам или фреймворкам, которые никто не использует. В отличие от этого, неуклюжий, но функциональный код часто содержит только правильные компромиссы для быстрой итерации. А это, в свою очередь, означает, что большое количество запутанного кода способствует созданию продуктов, которые нравятся людям, что является гораздо более сложной задачей.
Тест Роршаха
Я показывал код Flamework многим инженерам на протяжении многих лет, и обычно он вызывает такую бурную реакцию. Он ослепляет человека тем, что, кажется, игнорирует все правила хорошей программной инженерии.
Это делает Flamework увлекательным тестом Роршаха для инженеров. Смотрите ли вы на него с восхищением за то, что он сосредоточен на некоторых критических вопросах, таких как масштаб, встроенная наблюдаемость и инструменты отладки. Или вы осуждаете его и его создателей за то, что они вручную строят SQL-запросы, используют глобальные переменные, не используют классы и делают грязный код в стиле PHP4? Это прагматичный инструмент, намеренно созданный для быстрой итерации в масштабе, или это наивный беспорядок, созданный неумелыми разработчиками?
Стал бы я использовать Flamework? Конечно, нет. Но я ценю приоритеты, стоящие за ним. Если эти уродливые решения помогут вам двигаться быстрее, привлекать пользователей и утверждать продукт, то переписывание или большие рефакторинги впоследствии будут небольшой ценой.
Вопрос баланса
В конце концов, ваша позиция по поводу «дерьмового кода» зависит от вашей основной цели:
- Вы выпускаете продукт и спешите удовлетворить потребности пользователей?
- Или вы создаете многоразовую библиотеку или фреймворк, которые должны пройти испытание временем?
Оба подхода имеют право на существование, но они редко гармонично сосуществуют в одной кодовой базе. Flamework — это напоминание о том, что простые решения могут быть мощными, если они решают реальные проблемы. В конце концов, когда придет время, вы сможете привести его в порядок или перестроить с нуля.
Настоящий вызов заключается в том, чтобы решить, какой путь выбрать — и когда. Даже с опытом бывает трудно понять, когда нужно переходить от быстрых решений к более надежным основам. Принципы, лежащие в основе Flamework, также отражены в философии разработки Sentry. Один из самых ярких — «Прими изоленту».
Изолента — отличный инструмент для пробы новых вещей, и мы не должны бояться ее использовать. Иногда хорошо наклеенный скотч может прослужить дольше, чем сам элемент, а иногда это отличное решение, помогающее нам изучить то, что мы действительно хотим построить. Однако по мере взросления Sentry большая часть нашей клейкой ленты не выдержала испытания временем и она была заменена в те моменты, когда требовлаяс прочный бетонный фундамент.
Это потому, что успешные проекты в конце концов взрослеют. То, что позволяло быстро итерироваться в начале, в итоге может превратиться в неуправляемый бардак, и его придется переделывать изнутри.
Лично я никогда бы не создал Flamework, он меня немного отталкивает. В то же время я испытываю огромное уважение к людям, которые его создают. Их работа и мышление повлияли на то, как я решаю проблемы и думаю о разработке продуктов.