Если ориентироваться только на публичные обсуждения, может сложиться впечатление, что большинство проектов уже давно перешло на Spring Boot 3.x. В крайнем случае команда прямо сейчас выполняет миграцию, закрывает несовместимости и движется к актуальному стеку.
Но когда разговариваешь с инженерами не в формате доклада и не в режиме «расскажите, как у вас всё идеально устроено», картина оказывается менее однозначной.
На практике значительная часть систем всё ещё работает на Spring Boot 2.7 или на версиях из той же технологической эпохи. Новые сервисы могут запускаться на более свежем стеке, но старые продолжают жить на прежних версиях. И часто это не приводит к немедленной катастрофе: продакшен работает, инциденты не сыпятся каждую неделю, бизнес-процессы закрываются.
Просто публично такие детали обычно не выносят. Или говорят о них очень осторожно.
Отсюда появляется важный вопрос: если это распространённая практика, почему её так неудобно обсуждать? Почему факт использования старой версии фреймворка воспринимается почти как признание в инженерной неаккуратности?
Попробуем разобрать это без морализаторства.
Почему Spring Boot 2.7 неудобно обсуждать публично
На поверхности кажется, что причина сугубо техническая: старая версия, несовместимости, уязвимости, технический долг, устаревшие зависимости и код, который страшно трогать.
Но первый слой проблемы скорее организационно-культурный, чем технический.
Если команда остаётся на старой версии, это легко читается как сигнал, что процесс регулярных обновлений не встроен в разработку. А если обновления не встроены, значит кодовая база стареет, технический долг копится, изменения становятся дорогими, а в репозитории наверняка есть участки, которые никто не хочет трогать без крайней необходимости.
Обычно это не формулируют напрямую, но именно такое впечатление возникает часто.
В индустрии закрепилась некая норма: стек должен быть близок к актуальному. Не всегда потому, что конкретному проекту это сейчас необходимо, а потому что так выглядит здоровый инженерный процесс.
Spring Boot 2.7 в публичном разговоре звучит не как нейтральная техническая деталь, а как маркер отставания. Не обязательно стыдный, но точно неудобный.
В неформальном разговоре инженер может сказать: «Да, стек старый, миграция сложная, времени не выделили, риски понятны». Однако в официальной коммуникации быстро включается формулировка «мы на актуальных версиях» или хотя бы «мы уже планируем переход».
Есть и отдельная проблема с вопросом «зачем нам обновляться». Часто он воспринимается подозрительно, хотя сам по себе абсолютно корректен. Он ломает негласное допущение, что ценность обновления очевидна и не требует доказательств.
На практике это нормальный инженерный вопрос: не «давайте никогда ничего не обновлять», а «какую конкретную проблему мы решаем и какой ценой».
Если убрать эмоциональный фон, причин для обновления обычно немного.
-
Нужны возможности новой платформы, без которых уже сложно реализовать требования продукта.
-
Нужно закрывать уязвимости и поддерживать приемлемый security baseline.
-
Нужно получить исправления багов, улучшения производительности или поддержку актуальных зависимостей.
Остальное, как правило, является вариациями этих причин. И здесь появляется неудобная развилка.
Когда команда признаёт, что остаётся на старой версии, она как будто признаёт одно из трёх: новые возможности пока не нужны, обновление не проходит по приоритетам или security-риск закрывается не полной миграцией, а локальными мерами. Публично это звучит не очень удобно, поэтому проще не углубляться.
Почему миграция на Spring Boot 3.x оказывается дорогой
Есть распространённая ловушка: считать миграцию завершённой, когда проект компилируется. Для перехода на Spring Boot 3.x это слишком слабый критерий. Компиляция подтверждает только то, что устранён первый слой несовместимостей, но не то, что приложение сохранило прежнее поведение.
Иногда миграцию описывают как рядовое обновление зависимости: поднять версию, поправить импорты, прогнать тесты и закрыть задачу.
В реальном проекте change set обычно шире.
Первое — переход с javax на jakarta. Механика известная, но это не делает её бесплатной. После обновления проект перестаёт собираться, и команда проходит по коду, конфигурации, зависимостям и сторонним библиотекам, где старые API ещё используются. Задача понятная, но трудозатратная. И это только входной билет.
Дальше обновляется Hibernate. И здесь проблемы уже не ограничиваются ошибками компиляции. Меняется поведение ORM: генерация SQL, работа с маппингами, семантика HQL/JPQL, загрузка связей, типы, транзакционные сценарии. Приложение может не падать сразу, но начать работать немного иначе: ровно настолько, чтобы это пришлось ловить тестами или уже в эксплуатации.
Если в проекте нет интеграционных тестов с реальной базой данных, риски резко возрастают. Такие регрессии редко видны на этапе сборки или unit-тестов: запрос может начать генерировать другой SQL, маппинг — вести себя иначе, а ORM — выбирать другой план работы с данными. Без тестового контура это часто обнаруживается только после выкатки в прод.
Кроме того, Spring Boot 3.x требует Java 17+. Значит, обновляется не только Spring Boot, но и JDK, вследствие этого может изменится поведение и совместимость всего набора библиотек. Одни зависимости поддерживают Java 17 без вопросов, другие требуют обновления, третьи формально совместимы, но имеют нюансы в рантайме.
То есть на практике команда обновляет не одну строку в build-файле, а несколько слоёв системы:
-
Spring Boot и его автоконфигурацию;
-
JDK и runtime;
-
транзитивные зависимости и сторонние библиотеки;
-
поведенческие контракты приложения, которые могли не быть явно зафиксированы тестами.
После этого фраза «там просто версию поднять» звучит уже как сильное упрощение.
Как миграция выглядит в реальном проекте
В реальном проекте миграция с 2.7 на 3.x обычно выглядит как последовательное снятие нескольких классов риска.
Сначала не проходит сборка. Потом сборка проходит, но часть сценариев работает иначе. Затем выясняется, что критичные места нужно проверять на реальной базе. После этого всплывают несовместимости библиотек с новой Java. Потом становится понятно, что одних unit-тестов недостаточно и нужны интеграционные сценарии. В финале кто-то обязательно замечает, что это стоило планировать заранее.
Если в проекте заранее выстроены тесты, CI, миграции схемы, контроль зависимостей и observability, то жить проще. Но для среднего проекта это всё равно отдельная работа. Не маленькая задача между багфиксом и новой фичей, а полноценный кусок проекта с оценкой, планом и резервом на регрессии.
Отсюда возникает ключевой вопрос.
Почему компании остаются на 2.7
Короткий ответ: миграция конкурирует за инженерный ресурс.
Чтобы обновиться корректно, недостаточно понимать технический путь. Нужно встроить миграцию в реальный рабочий процесс: выделить людей, время, окно для проверки, среду для тестирования и понятный критерий готовности. Формат «посмотрите между задачами» для такого объёма обычно не работает.
Во многих командах есть условные 10–15% времени на технические задачи. На бумаге это выглядит разумно. На практике в этот же бюджет попадают багфиксы, инфраструктурные мелочи, последствия релизов, регламентные работы и срочные улучшения. Когда очередь доходит до большого обновления платформы, свободного ресурса часто уже нет.
Вторая причина — миграцию сложно обосновать бизнесу.
Когда разработка предлагает потратить время на обновление Spring Boot, бизнес ожидаемо спрашивает: какой измеримый результат мы получим и как это влияет на продукт, деньги или риск.
Ответить убедительно непросто. «Новые возможности фреймворка» не звучат как бизнес-ценность, если никто не показывает конкретный эффект. «Разработчикам станет удобнее» работает не во всех организациях. «Потом будет проще сопровождать» слишком часто воспринимается как отложенная и плохо измеримая выгода.
В результате получается сложная картина: обновление требует заметных усилий, несёт риск регрессий, а его ценность трудно описать в терминах, понятных за пределами разработки. Тогда главным аргументом становится безопасность.
Аргументы ИБ обычно проще формализовать. Если есть уязвимость, то её нужно закрывать. Вопрос в том, закрывать её миграцией всего стека или точечными действиями.
Поэтому security case часто становится единственным аргументом, который стабильно проходит через приоритизацию.
С ним проще получить согласование, потому что риск можно связать с инцидентом, аудитом, требованиями регуляторов или внутренними политиками.
Но дальше остаётся практический выбор: закрывать проблему большим обновлением платформы или минимальным изменением, которое снимает конкретную уязвимость.
Бизнес часто выбирает второй вариант. Если можно подобрать безопасную версию библиотеки, локально пропатчить зависимость или обойти проблему конфигурацией, это выглядит быстрее, понятнее и дешевле прямо сейчас.
Цена такого подхода в том, что каждое локальное решение добавляет в систему новое исключение. Здесь зависимость подняли не до актуальной версии, а до ближайшей безопасной. Там что-то исправили вручную. В другом месте оставили старую библиотеку из-за совместимости. Потом всё это живёт в документации, build-скриптах, знаниях отдельных людей и комментариях вида «не обновлять, иначе ломается».
По отдельности каждый манёвр может быть рациональным. Но через несколько итераций система превращается в набор частных случаев, которые нужно учитывать при любом следующем обновлении. Следующая миграция становится не просто большой, а существенно более дорогой и рискованной.
Но это следующее окно обновления будет когда-нибудь потом, а текущую проблему нужно закрывать сейчас. Поэтому такой компромисс понятен и часто встречается.
Ещё один распространённый компромисс: новые сервисы пишем на новом стеке, старые не трогаем. На архитектурной схеме это выглядит аккуратно. На практике старые сервисы никуда не исчезают: они остаются частью системы, участвуют в интеграциях, зависят от общих библиотек и часто служат шаблоном для новых сервисов. Если новые сервисы создаются клонированием старых, старый стек продолжает воспроизводиться даже без явного решения так делать.
Какие риски остаются
Первый риск — безопасность.
Не потому, что любой проект на Spring Boot 2.7 автоматически находится в критическом состоянии. Проблема в другом: со временем закрывать уязвимости становится сложнее, особенно если вместо регулярного процесса обновлений используется набор локальных исключений и ручных обходов.
Второй риск — рост эксплуатационной сложности. Каждое точечное исправление отдельно выглядит приемлемо, но система накапливает контекст. В какой-то момент поддержка старой платформы превращается из «ничего страшного» в отдельный постоянный поток работ.
Третий риск — ограничение дальнейшего развития. Сегодня возможности Spring Boot 3.x могут быть не нужны. Но позже может потребоваться новая Java, улучшения производительности, поддержка актуальных библиотек или возможности платформы, которых на старом стеке уже нет. Тогда выясняется, что вопрос был не в моде, а в постепенно сужающемся пространстве решений.
Есть и обратная сторона: если проблему игнорировать, система действительно может долго работать без видимых инцидентов. Старый стек не означает автоматического падения продакшена. Именно поэтому разговор о рисках часто выглядит неблагодарным: пока ничего не случилось, кажется, что обсуждать нечего.
Но отсутствие инцидента сегодня не означает отсутствия риска. Оно означает только то, что риск пока не реализовался.
Оставаться или мигрировать
Единого правильного ответа здесь нет.
Если проект активно развивается, если нужны новые возможности платформы или если безопасность уже стала заметным фактором, миграция, скорее всего, нужна. Но её нужно рассматривать как отдельный проект: с бюджетом, сроками, рисками, планом тестирования и критериями готовности.
Если проект стабилен, решает свои задачи, новых требований немного, а бизнес не готов инвестировать в обновление прямо сейчас, оставаться на 2.7 можно. Это не инженерный провал. Просто важно понимать, что покупается таким решением: не бесплатная стабильность, а отсрочка. Иногда оправданная, иногда нет.
Инициатива в таких вопросах почти всегда идёт от разработки: разработчиков, тимлидов, архитекторов, технических руководителей. Бизнесу обычно не важна версия Spring Boot как таковая, пока система работает и выполняет свои функции. Сверху чаще приходит только требование по безопасности или общий вопрос о стратегии обновлений.
Есть и человеческий фактор. Команды и отдельные инженеры часто оказываются между двумя крайностями.
-
Одни предпочитают держать стек максимально актуальным.
-
Другие откладывают обновления до последнего приемлемого момента.
Команды, сфокусированные на бизнес-функциональности, часто не хотят обновляться без явной необходимости: новые технические возможности им сами по себе не нужны. Это рациональная позиция, если при этом есть понятный источник багфиксов, исправлений безопасности и улучшений производительности с сохранением обратной совместимости.
Команды, ориентированные на новый стек, обычно хотят использовать технические нововведения сразу. Для новых проектов это естественно. Но когда проект стареет, те же люди могут потерять мотивацию регулярно обновлять его: стоимость изменений растёт, а эффект становится менее очевидным.
Выводы
Фраза «всем срочно мигрировать» слишком упрощает ситуацию. Фраза «можно бесконечно сидеть на старом стеке, если ничего не трогать» упрощает её не меньше.
На практике картина менее бинарная.
На Spring Boot 2.7 остаётся много проектов — чаще, чем это видно по публичным обсуждениям. Об этом не любят говорить, потому что вокруг темы смешались убеждение, что надо держать стек актуальным, организационные ограничения и нормальное желание не выглядеть отстающими.
При этом причины оставаться на старой версии обычно не сводятся к лени или некомпетентности. Чаще это время, деньги, риск регрессий, сложность обоснования и тот факт, что крупный апгрейд платформы действительно является крупным апгрейдом, а не галочкой «обновить зависимости».
Самый сильный реальный драйвер обновлений — безопасность. Остальные аргументы тоже важны, но обычно проходят через приоритизацию хуже.
Практичный вывод такой: оставаться на Spring Boot 2.7 само по себе не стыдно. Хуже — не понимать, почему вы на нём остаётесь. Ещё хуже — не понимать последствий. А делать вид, что вопроса вообще нет, — это уже не инженерная стратегия, а отсутствие стратегии.
ссылка на оригинал статьи https://habr.com/ru/articles/1044766/