Все чаще и чаще я вижу, что люди уклоняются от новейших технологий, делая выбор в пользу обратной совместимости. «Мы не можем повышать минимальные требования к PHP до 5.5, потому что у нас 50% пользователей еще на 5.4» говорят они. «Нет никакого способа обновиться до Guzzle 4+
, у нас бекенд на версии 3 и переделывать его слишком долго и дорого». И самый лучший аргумент от WordPress: «Мы не может придти к полному ООП, потому что большинство пользователей сидят на shared-хостингах с 5.1 или не знают про MVC».
Нонсенс.
Legacy-код – это большое НЕТ
Возможно, это спорный вывод, но я твердо уверен, нет места для legacy-кода в современных системах. Скажу несколько слов, прежде чем вы начнете точить свои вилы и зажжете факелы. Я имею ввиду, что не должно быть ни малейшего повода поддерживать старый функционал, вы добавляете обновления задним числом к старой версии только потому, что некоторые люди все еще используют ее. Даже если этих людей большинство — не делайте так.
Для уточнения: исправление ошибок предыдущих версий до тех пор, пока их контракт на долгосрочную поддержку не закончится, да. Добавление нового функционала, который можно выпустить в версии X
, в версии X-1
, только для того, чтобы не обижать пользователей X-1
— абсолютно и 100% нет. Аналогично, добавление X-1
кода в версию X только потому, что он может «пригодиться», должно быть признано недопустимым. Если вы по-прежнему берете с людей плату за X-1
и строите свои апгрейды поверх этого, то у вас очень плохой бизнес-план.
Хотя, кто я такой, чтобы нести подобную чушь? Я никогда не поддерживал большой проект с кучей заинтересованных сторон и саппортом, который развивается супермедленно и делает всех счастливыми. Насколько долго он делается и как работает не важно, ведь потенциально он может работать в 100 раз безопасней и в 1000 раз быстрее, верно? Не совсем. Моим самым большим ребенком был сайт крупного издателя со сложным бекендом на ZF1.
Если вы когда-нибудь делали проекты на ZF1
, то знаете, этот фреймворк — вихрь из костылей и анти-паттернов. Когда приложение начало показывать признаки ухудшения из-за увеличения трафика, я перестроил фронтенд наиболее интенсивно используемой части приложения полностью на работу через ajax и вызовы API. Нагрузка резко упала и этим мы купили себе достаточно времени для переноса всех остальных частей приложения на Zend Framework 2
. Тем, кто делал что-то подобное известно, что это все та же смесь из костылей и анти-паттернов, но чуть менее плотная.
Я пытаюсь сказать, что большие изменения и рефакторинг может произойти лишь в том случае, когда за ними стоят способные люди. Если все что вы делаете — это сплошные agile-митинги и мозговые штурмы, то никакое количество LTS контрактов не может помешать вам глупо выглядеть в течение пяти лет.
Даже если вы делаете бесплатную и/или open source работу, конечно же вы не должны ломать совместимость для X-1 пользователей. Делая им одолжение, развивая старые версии, при глобальном обновлении вы можете столкнуться с потенциальной потерей обратной совместимости. Просто усвойте одну вещь — они должны либо приспособиться, либо умереть.
Так все же почему мы должны изгнать legacy-код из современных систем?
Бесконечное LTS проклятие
Подписываясь под подходом «поддерживаем все так долго, как только можем», вы хоронитесь в бездонную яму, и, глядя на себя через несколько лет, когда вы окажитесь вынуждены поддерживать четыре различные версии вашего продукта, вы будете биться головой об стену из-за того, что не отказались от пользователей V1 и V2, если могли. В попытке сохранить аудиторию разработчики часто выходят за рамки своих возможностей и несправляются под гнетом тонн legacy-кода. По той же причине WordPress и оказался в своем нынешнем состоянии. Не позволяйте приковывать себя к старой версии.
Эти пользователи — мертвый груз, они должны быть уничтожены вне зависимости от того сколько денег они вам приносят. Дайте им возможность перехода и двигайтесь дальше, если они способны, то догонят вас. Если же нет, то они того не стоят.
Поддерживая старые версии слишком долго, вы насылаете на себя WP-проклятье. Старые версии уязвимы, их поддержка требует все больше сил и усилий по исправлению багов. Потратьте лучше эти часы на создание новых версий и наймите разработчиков, которые будут помогать пользователям с переходом.
Вы отчуждаетесь и негативно влияете на продвинутых пользователей
В последний раз я сталкивался с отчаянным legacy-кодом я сталкивался, когда устанавливал CMS
, что оказалось очень трудным занятием в среде Vagrant
— не только из-за проблем с symlink
, которые сейчас широко известны всем (даже создателю Vagrant
), но и с тем, что многие оставляют устаревшие версии CMS
, т.к. часть модулей/плагинов еще не выпустили свои обновления. Раз нет обновлений модулей, то к чему обновлять ядро? Зачем торопить события, если ты к ним не готов?
Оставляя legacy-код в новой версии, вы в конечном итоге получаете монстра Франкенштейна, который вроде как по-прежнему работает, но плохо, и новый код, имеющий потенциал, не может его развить из-за беспорядка в унаследованной кодовой базе. Такой подход хоть и делает работу компаний-разработчиков, которые все еще застряли где-то в 90-х, проще, но в тоже время продвинутым пользователям становится сложнее работать с продуктом. Решениями, принятыми для толпы, которая уже давно и безнадежно отстала от технологий, вы отчуждаете и негативно влияете на продвинутых пользователей, способных принести гораздо большие деньги.
Вы же знаете как это бывает: тратите слишком много времени на поддержку функционала в устаревших браузерах, а в итоге никто из пользователей даже не пользуется этими фичами. То же самое касается и пользователей библиотек или систем управления контентом — тех, кто не заботится об устаревшей CMS
, не волнует, что вы делаете в новых версиях, так что не парьтесь с черезмерной поддержкой старых версий больше, чем это нужно в реальности.
Неудачи иногда предвещают успех
Конечно, иногда это просто невозможно, и подобные исключения являются очень редким и ценным обучающим материалом. Один из любопытных случаев версионирования — 2й и 3й Python. Python — это удивительный язык, с ним вы можете сделать практически все что угодно. Но он не будет работать также как мог бы язык, построенный специально для вашей цели, это общий недостаток «мастер-на-все-руки» языков — они могут сделать что-то очень хорошо, но нет ни одного варианта сделать работу с помощью них безупречно. Когда пришел Python 3, в него были введены некоторые фичи, ломающие совместимость и пользователи 2й версии уже не могли так просто перейти на него.
Большинство отговорок вроде «пока еще не хватает Py3 пакетов» и «у нас слишком много кода в Py2, чтобы переписать все это» получают от меня ответы — «портируйте то, что вам нужно» и «Бедные программисты, их заставляют писать код», соответственно. Согласен, тут было несколько аргументов, но и те, как правило, берут в пример проекты, которые были изначально неправильно спроектированы, что и вылилось в их абсурдно большой размер.
На самом деле сейчас противостояние Py2 против Py3 уже превратилось в разлом, по обеим сторонам которого стоят программисты. Но многие не задумываются о том, что к тому времени как будет Python 4, люди, которые так яростно отказывались от перехода на версию 3+ будут по прежнему оставаться на Py2 и несовместимость станет еще больше. К тому моменту они могли бы уже освоить другой язык, а не противиться изменениям в текущем. С другой стороны, те, кто «отважился» переступить через разлом и переписал свой код на 3+ без долгих колебаний, получат все новейшие возможности будущих версий с нулевыми трудозатратами.
Слом обратной совместимости достаточно эффективно отсек ленивых и подготовил Python для нового поколения разработчиков. На мой взгляд, это огромный успех. Программирование, как и многие другие сферы жизни, все еще основано на выживании наиболее приспособленных — и если вы не можете использовать новые технологии адекватно — уйдите с дороги, не мешайте тем, кто может.
Приложения vs Библиотеки/Пакеты
Также есть вопросы по поводу противостояния приложений и библиотек. Другими словами, если правило «не устаревать» применимо для приложений, например при получении нового релиза фреймворка, то должно ли оно распространяться и на библиотеки?
Да.
Библиотеки, получившие бамп X+1
, должны четко следовать пути к прогрессу — с момента, когда ваша версия библиотеки стала публично доступна, поддерживайте только багфиксы последней.
Приложения, которые используют такие библиотеки, находятся в более сложной ситуации из-за их зависимости от API, которые, возможно, могут измениться. Разумный подходом будет ждать отзывов из комьюнити о стабильности, прежде чем начать переход. В течении переходного периода, как старая, так и новая версии библиотеки/фреймворка могут оставаться в использовании, и после того, как все необходимые части будут модернизированы, старая версия должна быть удалена. Ведь это не займет много времени, правда?
Не бывает достаточно большого приложения
«Но Бруно, некоторые приложения огромны и их переписывание займет несколько месяцев», скажете вы. «Переход с ZF1 на ZF2 — это год работы!», на что я отвечаю: чушь. Нет достаточно большого веб-приложения, апгрейд которого займет подобные сроки. Я пойду еще дальше и скажу, что на самом деле нет веб-приложений достаточно больших, которые могли бы сравниться по размерам с Symfony или Zend.
Конечно же, все что я говорил, не относится к какому-либо из этих фреймворков, они гиперкомплексные бегемоты из очень профессионального кода, но если вы следуете концепции разделения задач, инкапсулируете свои сервисы и APIфицируете ваше приложение, то вы можете писать фронт отдельно от бекенда, что, в свою очередь, освобождает вас от многих преград на пути к актуальному коду независимо от размера и/или популярности приложения — при условии, что у вас в команде есть программисты, а не теоретики. Неважно сколько структур и алгоритмов вы знаете — главное знать как их использовать, чтобы быть достаточно эффективным во время миграций.
Схема обновления
Как правильнее всего обновляться? Есть только один приемлимый вариант обновления программного обеспечения, которого должны придерживаться разработчики независимо от популярности их приложения/библиотеки:
- Создайте новую ветку для новой версии
- Пометьте старую версию/ветку как deprecated
- Публично заявите, сколько вы еще будете поддерживать старую версию
- Предупредите всех пользователей старой версии
- Внедряйте новые функции ТОЛЬКО в новой ветке, в старой — багфиксы
- Когда время жизни старой версии истечет, обрывайте все концы. Не исправляйте, не советуйте, вообще удалите упоминание о старой версии из документации. Убейте ее.
После прохождения данной процедуры вы никогда не получите в наследство неприятности.
Один из проектов, следущий этому пути — Guzzle. У него есть 3+ версия и 4+, для тех, кто хочет жить в ногу со временем и всегда быть на пике прогресса.
Вывод
Как веб-разработчик, я твердо уверен, legacy-код должен быть выброшен, когда речь заходит о новых фичах или мажорном обновлении версии. Если у вас есть большой проект, которое использует код версий, вышедших два или более месяцев назад, то вы должны прекратить все действия с ним и переписать под свежую версию, особенно, если продукты, которыми вы пользуетесь, критически важны для бизнеса. Нет в мире достаточно больших приложений, которым нужно более двух месяцев для полного перехода, а если и есть, то они должны быть переписаны с нуля — веб намного проще, чем вы всегда предполагали.
А что думаете вы? Следует ли legacy-код хранить неограниченное время? Определенное число версий? Возможно не все? Как вы относитесь к legacy-коду в сторонних проектах? Должен ли разработчик волноваться о проблемах с legacy в библиотеках, которыми он пользуется? Ошибся ли я в этой статье? Данным постом я хотел начать дискуссию об этом — в конце концов, мои взгляды на проблемы и переживания — лишь моя точка зрения. Дайте знать в комментариях ниже.
Автор: iGusev