Конечно, можно много рассуждать про плохие архитектуры, про то, как предыдущие поколения разработчиков умели только груши околачивать, а за их работу четвертование - самое лёгкое наказание. Но есть нюанс. О нём и расскажу.
Когда создаётся продукт
Когда начинает создаваться новый продукт, обычно, для него формируется нечто вроде рамок, которые надо заполнить функциональностью. Обычно, проговаривается, что он должен уметь делать и, вероятней всего, как это должно происходить. В проектном управлении это что-то вроде объединённых "видения" и "технических требований" (поясню, первое - это что-то вроде что именно мы хотим получить, а второе - это из каких узлов будет это состоять). Следующим этапом, как правило идёт техническое задание, но его пишут только для очень хорошо отточенных и требовательным к результату систем, вроде систем управления спутниками на орбите или критического программного обеспечения для автомобилей (китайцы, если судить по количеству обновлений прошивки, видимо, этот этап тоже пропускают).
И вот, для команды разработки спускается недооформленный документ, почему-то, названный техническим заданием, который примерно говорит, что надо делать, но, чаще всего, не уточняет, как это делать. Уточню, хотя такое бывает очень часто, но далеко не всегда; справедливости ради, хочу отметить, что бывает ситуация хуже, когда "видение" будущего проекта спускается как оформленное техзадание: что хочешь с этим, то и делай.
И в дело вступает архитектор
Наверное вы представили себе бородатого мужика в очках... ну я, по крайней мере их так раньше представлял; однако часто это бывает не так. Берётся наиболее подготовленный (или просто наиболее бойкий) представитель команды и рисует квадратики, которые будут модулями системы. Иногда он прорабатывает это детально и, вроде как, даже использует инструменты вроде Rational Rose Enterprise, но это бывает очень на часто.
Намного чаще случается такая ситуация: "а, тут всё понятно, что надо делать; сейчас я за полчаса напишу скелет системы, а дальше его обвесим всяким". И ведь пишет, чертяка.
Наверное, написанное выше выглядит как-то... ненадёжно, но, на удивление, это не так. Такой "получасовой" проект, нередко, проживает долгие десятилетия.
Первые сложности
На самом деле, практически неважно, как была сформирована первичная модель архитектуры - кавалерийским наскоком или детальной проработкой текущих требований. Проблемы возникнут при обоих подходах, но главная трудность находится совсем в ином измерении.
Дело в том, что любое программное обеспечение - это бизнес или, проще говоря, прямой либо косвенный заработок денег (либо экономия, но это неважно в данном случае). Когда создаётся новый продукт, перед бизнесом стоит две задачи: как можно быстрее вывести его на рынок и, оценив перспектив, принять решение о целесообразности и размере дальнейших инвестиций в продукт. Поэтому, для начала, нередко, требуется создать малофункциональный прототип.
Приведу пример: вы решили создать что-то вроде текстового редактора Word, но, чтобы оценить перспективность, сперва сделали его упрощённую версию - Notepad.
И вот, когда прототип, что называется, "выстрелил" - бизнес принимает решение - продолжаем функционирование. И сразу появляется расширенный список функциональных требований, к которым спроектированная архитектура была не готова.
Криворучкам тут не место!
В первый момент хочется возмутиться, мол, что за оборванцы создавали архитектуру, если не предусмотрели её расширения? И это справедливый вопрос. Ответ на него, как правило, не очевиден, но в большинстве случаев его можно свести к утверждению: расширение функциональности планировалось, но не планировались такие масштабы.
Ещё пример: вы решили построить дачный домик. Понимая, что, скорее всего, захотите два этажа и мансарду - заложили усиленный фундамент и сделали стены первого этажа попрочнее, а крышу - легко демонтируемой. Но вам и в голову не приходило, что через 5 лет тут будет стоять 12-этажный дом...
Иными словами, каким бы опытным не был бы архитектор, у него всегда есть границы, в которых он мыслит и рамки, за которые приложение не может запросто выйти. А эти самые границы могут быть обнаружены относительно быстро.
Временные ограничения также никто не отменял: теоретически, можно написать достаточно гибкую и достаточно широко масштабируемую систему, позволяющую расти приложению, что называется, в разные стороны, практически не натыкаясь на ограничения, но тут встаёт вопрос ресурсов. Такие гибкие системы имеют два существенных недостатка: их долго делать и с ними сложно работать.
Долго их делать потому, что требуется учесть и обработать много гипотетических входных условий. Сложно - потому, что каждый новый шаг требует тщательного встраивания, что плодит длинные цепочки абстракций и сопряжений.
Проще говоря бизнес на это никогда не идёт.
Костыли и велосипеды
Расскажу историю из своей практики. Мы разрабатывали СКАДА систему (платформу, если быть точнее) и был установлен некоторый набор примитивов, которые надо было отображать. В качестве способа отрисовки - была выбран SVG компонент (HTML5) и он более чем достойно справился с задачей.
Однако, через некоторое время возник следующий проект, масштаб которого был сильно больше, чем предыдущий. То есть он не просто был больше, а в десятки раз больше. Вдобавок, появились дополнительные требования и протестированный предел элементов на схеме, который, в теории, никогда не должен был быть достигнут - был достигнут. Пришлось приспосабливаться налету.
Немного пояснений: браузеры (а мы отображали всю информацию в браузере) имеют что-то вроде эффективного количества обрабатываемых элементов. Когда их становится слишком много - браузер начинает тормозить (попробуйте открыть в последнем хроме документ с более чем 2000-5000 тэгов - поймёте о чём я).
Могли ли мы это учесть в самом начале работ? Нет. Потому что количество неизвестного в тот момент было сильно больше, чем количество известного. И у нас, к слову, был отличный архитектор и аналитик, которые до этого целый год проектировали систему и оформляли технические требования. Но это нисколько не помогло нам в процессе работы, т.к. по мере создания продукта открывались новые ограничения или дополнительные условия, которые нельзя было предусмотреть.
Иными словами, ещё относительно свежий продукт начал обрастать своими костылями и велосипедами.
Продолжение истории
В ИТ среде есть легенда, что первая версия продукта пишется в помойку, а вот вторая - вторая уже да, она рабочая версия.
Возможно, где-то это и так, но, чаще всего, бизнес смотрит на результат, выслушивает доводы ботаников и, что называется, отрабатывает возражения. Недостаточно хорошо работает? Но ведь работает! Сложно поддерживать из-за накопившегося технического долга? А вот вам целых два дня в месяц на его закрытие! Не успеваете в срок из-за сложности поддержки? Ну ладно, вот вам ещё человек в команду! Клиенты рыдают из-за ошибок? Ну пусть в команде будет тестировщик! Но новые релизы должны выходить регулярно!
Короче говоря, написать новую и улучшенную версию продукта, чаще всего, не дают. Потому что, даже для того, чтобы оставаться на месте - надо бежать изо всех сил...
Увеличение сложности поддержки
Спустя какое-то количество выпусков новых версий - накапливается критических технический долг. Это не значит две вещи: каждая новая функциональность требует проверки большого количества соседствующих компонентов, потому что связи перестают быть очевидными. И вторая - чтобы добавить что-то новое, слишком много надо перестраивать, попутно ломая (и исправляя) смежные функциональные блоки.
Обычно, это сильно снижает мотивацию команды. Представьте себе ситуацию, что смена цвета кнопки требует месяц работы. Абсурд? Конечно! А каково разработчикам? Вот они и начинают сливаться.
Текучка порождает ещё больше проблем: новенькие, не зная всей предыстории, начинают вносить свои корректировки, попутно ломая многие старые неявные связи, которые проявляются в самых непредсказуемых местах. Из-за этого на них сваливаются другие, совсем непонятные задачи, которые заставляют их искать лучшей доли в других компаниях.
Апофеозом данного направления становится гибель продукта.
Выход есть!
Как ни странно, выход из данной ситуации имеется, но он отнюдь не прост. И начинается он с формализации требований к компонентам. Проще говоря: что и как компонент делает и где лежит функциональность.
Разработчик ныряет в эту "кашу" и постепенно приводит её в порядок. Понятно, что всё сразу изменить нельзя, поэтому сперва приложение дробится на более-менее понятные компоненты, затем, внутри компонентов, выделяются функциональные блоки, которые компонуются и осмысляются. Чаще всего, это приходит к вымарыванию большого количества "мёртвого" кода и ненужных библиотек.
Главная сложность этого процесса в том, что всё время система продолжает развиваться и, поэтому, разделённые компоненты должны преобразовываться либо очень быстро, чтобы успеть между релизами, либо замораживаться для новых изменений до полного изменения, а все требуемые модификации выполняются как-то иначе (обычно, компоненты зависят друг от друга и изменения в компоненте А могут потребовать изменений в смежном компоненте Б, который, в настоящий момент, находится на реконструкции). Чаще всего, это достигается тем, что для обновляемого компонента делается система расширения, которая позволяет вносить срочные изменения; позже она интегрируется в изменённый компонент.
В данном подходе, также, очень уместно использовать разработку через тестирование (TDD), так как она, гипотетически, позволит избежать будущих проблем. Но бизнес, скорее всего, на это дополнительных ресурсов не выделит. Так что играть придётся на свой страх и риск.
Не всегда нужно менять всё
Ещё одна правда состоит в том, что далеко не всегда требуется тотальное обновление. Иногда сильно дешевле прекратить следовать существующим канонам, начав вместо этого использовать "вынос функциональности": вместо того, чтобы переделывать компонент целиком, создавать параллельные компоненты, учитывающие возросшие требования к приложению. Вся спорная или обновляемая функциональность плавно переносится туда и поддержка её сильно облегчается.
Если такой подход приживается, то это сильно облегчает поддержку продукта и не требует ресурсов на глобальные переделки. Может быть не особо правильно с точки зрения разработки, но очень приветствуется с точки зрения бизнеса.