Як ми мігрували проєкт на Spring Boot 3

💡 Усі статті, обговорення, новини про Java — в одному місці. Приєднуйтесь до Java спільноти!

Всім привіт. Я Сергій Моренець, розробник, викладач, спікер та технічний письменник, хочу поділитися з вами своїм досвідом роботи з такою цікавою темою, як міграція застосунків.

Ця тема рідко зустрічається в статтях, оскільки у більшості випадків міграція є тривіальним процесом. Але Spring Boot 3 стоїть окремо в цьому списку, позаяк тут була порушена зворотна сумісність з попередніми версіями, причому порушена неодноразово.

Ситуацію ускладнює те, що мало хто використовує чистий Spring Boot, а скоріше у зв’язці зі Spring MVC, Spring Data, Spring Cloud, які також потребують міграції. Тому в цій статті я хотів би поділитися досвідом у переході кількох своїх проєктів на нову версію Spring Boot, тим більше, що ми торкаємося цієї теми на деяких наших тренінгах.

Зміни у Spring Boot 3

Є кілька Java-проєктів, де головні (major) версії випускають дуже рідко, ретельно тестуються і вітається фідбек розробників про milestone (beta) версії. Цим може похвалитися і Hibernate, і лінійка Spring/Spring Boot проєктів.

Попередня версія Spring Boot 2.0 була випущена в березні 2018 року, а поточна 3.0 — через 4 роки, у листопаді 2022 року. Змін, як завжди, було дуже багато, деякі з них були несумісні з попередньою версією, тому довелося випустити спеціальний посібник з міграції. Які ж зміни були найістотнішими?

  1. Перехід на Spring Framework 6. Тут відбулася істотна внутрішня зміна конфігурації — відмова від конфігураційного файлу spring.factories на користь нового файлу (і формату) для автоконфігурації — META-INF/spring/org.springframework.boot.autoconfigure. AutoConfiguration.imports. Крім того, деякі налаштування було перейменовано.
  2. Використання JDK 17 як мінімально припустимої версії. Як ми пам’ятаємо, Spring 5 став однією з перших Java-технологій, яка перейшла на JDK 8. І ось тепер Spring 6/Spring Boot 3 стали першими великими Java-проєктами, які підняли планку до JDK 17. Чому не до JDK 11? Насамперед, LTS-версії стали випускатися набагато частіше. Наприклад, JDK 21 вийде вже цієї осені. По-друге, в JDK 12-17 було багато цікавих фітч (Java Records, pattern matching, текстові блоки, switch expressions), які спрощували розробку самого Spring. Зрозуміло, вам теж доведеться перейти на JDK 17, якщо ви хочете використати Spring 6/Spring Boot 3.
  3. Перехід на Jakarta EE 9. Цей перехід був найболючішим, оскільки Jakarta EE 9 несумісний з попередньою версією (ребрендинг javax.*->Jakarta.*). Так що доведеться оновити всі імпорти та багато налаштувань, пов’язаних з Enterprise Java. Крім того, доведеться перейти на ті enterprise технології, які сумісні з Jakarta EE 9/10, оскільки Spring 6 перестав підтримувати Hibernate 5, а підтримує тільки Hibernate 6 (або ті версії Hibernate 5.x, які сумісні з Jakarta EE 9).
  4. Стабільна підтримка GraalVM Native images. Якщо раніше доводилося використовувати окрему залежність Spring Native, то тепер цей проєкт оголошено deprecated, а підтримка GraalVM реалізована в Spring і Spring Boot плагінах.
  5. Підтримка нового Observation API від Micrometer.

З цього списку, якщо не брати до уваги інфраструктурні зміни (JDK 17, Jakarta EE 9), найцікавішою фічею є стабільна підтримка GraalVM (версії 22.x), яку робили майже 3 роки.

Способи міграції

Робити міграцію на нову версію, коли йдеться про порушення зворотної сумісності, простою зміною версії в скрипті збірки небезпечно. Хоча в нас і є посібник з міграції, доведеться скрупульозно по ньому йти, перейменовуючи імпорти та налаштування. А раптом забудемо перейменувати якусь властивість? Адже жодних помилок (компіляції чи run-time) не буде.

Чи є автоматизований спосіб, наприклад Angular CLI з його schematics? Є, більш того, є навіть способи:

  1. Бібліотека OpenRewrite.
  2. Проєкт Spring Boot Migrator.

OpenRewrite — це порівняно новий проєкт, який стартував у 2020 році і позиціонує себе як бібліотека для повномасштабного рефакторингу коду та конфігурації. Вона написана на Java, але з коробки підтримує Python, Kotlin, SQL і навіть Terraform.

При цьому не потрібно помилятися щодо рефакторингу. OpenRewrite зовсім не призначений для перейменування класів і методів, він дозволяє здійснювати безпечну міграцію на нові версії ПЗ, проводити статичний аналіз коду, оновлювати ваш API та багато іншого. Сама бібліотека надає базові можливості, а різні готові розширення вже адаптовані для прикладних «рецептів»:

  1. міграція на JDK 17;
  2. перехід із Junit 4 на Junit 5;
  3. міграція з Log4j на Slf4j;
  4. перехід із Micronaut 2 на Micronaut 3.

Кожен «рецепт» може включати інші рецепти і складається з двох операцій: пошук і трансформацію коду (конфігурації). При цьому реалізовано це досить гнучко. Сам рецепт — це Java-клас, але який можна додатково налаштувати у YAML-файлі. Ось як виглядає конфігурація рецепту, який видаляє deprecated властивості:

type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.spring.boot3.ActuatorEndpointSanitization
displayName: Remove the deprecated properties `additional-keys-to-sanitize` from the `config-props` and `env` end points
description: Spring Boot 3.0 removed the key-based sanitization mechanism used in Spring Boot 2.x in favor of a unified approach. See 

Github

recipeList:
- org.openrewrite.java.spring.DeleteSpringProperty:
propertyKey: management.endpoint.configprops.additional-keys-to-sanitize
- org.openrewrite.java.spring.DeleteSpringProperty:
propertyKey: management.endpoint.env.additional-keys-to-sanitize

Як бачите, всі назви властивостей не прописуються жорстко в коді, а виносяться в YAML і це дозволяє, по-перше, перевикористовувати базові рецепти, по-друге, швидше розробляти нові рецепти.

Для Java-проєктів є дві головні бібліотеки — rewrite-migrate-java та rewrite-spring, остання нам і потрібна.

Може здатися надмірним використовувати окрему бібліотеку, якщо потрібно просто поміняти версію JDK на 17 для проєкту, але список рецептів тут досить великий:

- org.openrewrite.java.migrate.Java8toJava11
- org.openrewrite.java.migrate.JavaVersion17
- org.openrewrite.java.migrate.lang.StringFormatted
- org.openrewrite.java.migrate.lombok.UpdateLombokToJava17
- org.openrewrite.github.SetupJavaUpgradeJavaVersion
- org.openrewrite.java.cleanup.InstanceOfPatternMatch
- org.openrewrite.java.migrate.lang.UseTextBlocks

І він включає, наприклад, явне застосування тих фітч, які з’явилися з JDK 9 JDK 17 — текстові блоки, pattern matching, заміна Collections.singletonList() на List.of() і т.д. Є й складніші рецепти — наприклад, заміна типів з Google Guava на стандартні колекції/Optional JDK.

По суті, такий рецепт заощаджує ваш час і позбавляє рутинних помилок. Якщо повернутись до міграції на Spring Boot 3, то тут список рецептів ще більше:

- org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7
- org.openrewrite.java.spring.boot3.RemoveEnableBatchProcessing
- org.openrewrite.java.spring.boot3.MavenPomUpgrade
- org.openrewrite.java.migrate.UpgradeToJava17
- org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta
- org.openrewrite.java.spring.boot3.RemoveConstructorBindingAnnotation
- org.openrewrite.java.spring.boot2.MoveAutoConfigurationToImportsFile
- org.openrewrite.java.spring.boot3.ActuatorEndpointSanitization
- org.openrewrite.java.spring.boot3.MigrateMaxHttpHeaderSize
- org.openrewrite.java.spring.boot3.DowngradeServletApiWhenUsingJetty
- org.openrewrite.java.spring.boot3.ConfigurationOverEnableSecurity
- org.openrewrite.java.spring.boot3.SpringBootProperties_3_0_0
- org.openrewrite.java.spring.boot3.MigrateThymeleafDependencies
- org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0

14 рецептів — це багато чи мало? Дивлячись, з чим порівнювати. Наприклад, рецепт міграції на Jakar-ta EE 9 (JavaxMigrationToJakarta) містить 33(!) рецепти.

Використовувати OpenRewrite дуже просто. Достатньо лише додати OpenRewrite плагін і вказати потрібні назви рецептів. Вся трансформація буде проведена під час мануального запуску (активації) відповідного плагіна, причому OpenRewrite гарантує збереження оригінального форматування коду.

Spring Boot Migrator (SBM) — це експериментальний проєкт, створений 2022 року, швидше за все якраз під вихід Spring Boot 3. Він використовує OpenRewrite під капотом, але якщо OpenRewrite робить всі зміни відразу і автоматично, то SBM може працювати в двох режимах:

  1. Інтерактивний CLI.
  2. Зручний Web UI.

На відміну від SBM тут ви можете подивитися список запропонованих рецептів і застосувати тільки один з них. Крім того, SBM вміє і деякі цікавіші речі, наприклад, міграцію з Java EE застосунків на Spring Boot.

Міграція Spring Boot 3

Спочатку ми спробували використати OpenRewrite:

       <build>
              <plugins>
             <plugin>
                    <groupId>org.openrewrite.maven</groupId>
                    <artifactId>rewrite-maven-plugin</artifactId>
                    <version>4.42.0</version>
                    <configuration>
                           <activeRecipes>
                           <recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0</recipe>
                           </activeRecipes>
                    </configuration>
                    <dependencies>
                           <dependency>
                           <groupId>org.openrewrite.recipe</groupId>
                           <artifactId>rewrite-spring</artifactId>
                           <version>4.34.0</version>
                           </dependency>
                    </dependencies>
             </plugin>
              </plugins>
       </build>

Тут ми вказали ідентифікатор плагіна, назву головного рецепту та потрібну залежність, де зберігаються рецепти (rewrite-spring). Для старту міграції достатньо цієї команди:

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run

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

mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:LATEST -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0

Сканування та заміна йдуть не так швидко, крім того, як виявилося OpenRewrite, не тільки додає нові залежності, а намагається і завантажити їх.

Проаналізувавши зміни, можна побачити багато цікавого. Наприклад, OpenRewrite застосував деякі рецепти так званих «best practices», які ніяк не пов’язані з Spring Boot. Для Junit тестів він, по-перше, видалив модифікатор public із тестових класів. По-друге, застосував стандарт RSPEC-3415 із Sonar, в якому в assertion спочатку має йти очікуване значення, а потім актуальне:

              assertEquals(OrderState.COMPLETED,order.getState());

а не:

              assertEquals(order.getState(), OrderState.COMPLETED);

Хоча це не впливає на роботу тесту, але робить його зрозумілішим. Як OpenRewrite відрізняє ці два значення? Якщо він бачить поле static/final/enumeration, то вважає, що це очікуване значення. Ще одна корисна зміна:

return String.format(format, getClass().getSimpleName(), id);

на

return format.formatted(getClass().getSimpleName(), id);

Метод formatted класу String з’явився у JDK 15.

Є й складніші зміни. Наприклад, у ті Spring біни, які оголошені через @Bean і залежать від біна типу DataSource, додається анотація @DependsOnDatabaseInitialization. Вона каже Spring ініціалізувати такі біни тільки після ініціалізації бази даних.

Також OpenRewrite поміняв імпорти javax.* -> Jakarta.*, але зробив це не механічною заміною, тому що, наприклад, у JDK є теж пакети javax.*. Тому тут потрібно брати кореневий пакет для кожної специфікації з Jakarta EE: javax.validation, javax.persistence і т.д.

Тут же випливли й деякі неточності. Наприклад, якщо є окремі властивості для версій Spring проєктів, то вони не змінилися:

              <spring.boot.version>2.7.1</spring.boot.version>
              <spring.cloud.version>3.1.1</spring.cloud.version>
              <spring.retry.version>1.3.3</spring.retry.version>
              <spring.kafka.version>2.8.7</spring.kafka.version>

Більш того, мені здалося, що OpenRewrite не вміє працювати із залежностями Spring Cloud та деяких інших Spring-проєктів.

У деяких випадках він не зміг завантажити залежність і вставляв коментар у pom.xml (що добре), але таких випадків було занадто багато:

              <!--~~(Unable to download POM. Tried repositories:
https://repo.maven.apache.org/maven2: HTTP 404)~~>--><!--~~(Unable to download POM. Tried repositories:
https://repo.maven.apache.org/maven2: Did not attempt to download because of a previous failure to retrieve from this repository.)~~>--><dependency>
                     <groupId>jakarta.servlet</groupId>
                     <artifactId>jakarta.servlet-api</artifactId>
                     <version>5.0.3</version>
              </dependency>

Іноді він вставляв абсолютно непотрібні залежності:

              <dependency>
                     <groupId>org.glassfish.jaxb</groupId>
                     <artifactId>jaxb-runtime</artifactId>
                     <version>4.0.2</version>
                     <scope>provided</scope>
              </dependency>

Невідомо чому, можливо, через наявність Junit 5 у проєкті, OpenRewrite вставив exclusion, який видаляє Junit 4 з транзитивних залежностей:

              <dependency>
                     <groupId>org.testcontainers</groupId>
                     <artifactId>kafka</artifactId>
                     <scope>test</scope>
                     <exclusions>
                           <exclusion>
                                  <groupId>junit</groupId>
                                  <artifactId>junit</artifactId>
                           </exclusion>
                     </exclusions>
              </dependency>

Це досить мило, але така турбота за чистоту конфігурації призвела до падіння тестів, оскільки саме в контейнері Kafka Junit 4 все ще використовувався, так що довелося це видалити.

OpenRewrite може не лише додавати, а й змінювати залежності. Наприклад, нещодавно змінився MySQL connector для Java з:

                     <groupId>mysql</groupId>
                     <artifactId>mysql-connector-java</artifactId>

на:

                     <groupId>com.mysql</groupId>
                     <artifactId>mysql-connector-j</artifactId>

OpenRewrite змінив і це залежність. Але він не може робити ті заміни, де потрібне схвалення людини. Наприклад, у Spring 6 видалили клас CommonsMultipartResolver, оскільки mul-tipart тепер підтримується на рівні контейнерів сервлетів. Автор Spring рекомендує тепер використовувати клас StandardServletMultipartResolver, але такий вибір має зробити розробник, а не плагін.

І в кінці роботи OpenRewrite плагін видаляється з pom.xml. Для іншого проєкту ми протестували Gradle плагін:

plugins {
    id("org.openrewrite.rewrite") version("5.38.0")
}
 
rewrite {
    activeRecipe("org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0")
}
 
dependencies {
    rewrite("org.openrewrite.recipe:rewrite-spring:4.34.0")
}

Одна з переваг OpenRewrite — це можливість запуску в так званому «dry» режимі, коли вихідні файли не змінюються, але створюється patch-файл з усіма змінами плюс вказуються ті рецепти, які були застосовані для кожного випадку.

Запускаємо «dry» режим: gradle rewriteDryRun і виявляється, що дефолтних 512 Мб для роботи йому не вистачає, довелося вказати 2 Гб. Через три хвилини роботи створився patch-файл розміром 255 кб.

На жаль, зміни були тільки для Java-файлів, скрипти збірки на Groovy виявилися незайманими. Це зрозуміло, оскільки парсинг таких скриптів по суті потребує їх компіляції, і все це набагато складніше зробити, ніж для pom.xml в Maven.

У чомусь OpenRewrite мені здався подібним до Liquibase, але всі результати його роботи вимагають ретельної перевірки. Тим не менш, його колекція «best practices» захоплює, і він справді може внести чимало корисних змін до вашого проєкту.

Тепер спробуємо Spring Boot Migrator (SBM). Так як це утиліта, то її потрібно завантажити і почнемо з Web UI, який називається Spring Boot Upgrade.

Розмір вселяє повагу — 153 Мб. Включає базу даних RockDB, JRuby та ICU4j (робота з UTF). Запускаємо його:

java -jar --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens ja-va.base/java.io=ALL-UNNAMED spring-boot-upgrade.jar .

Відразу помилка, тому що OpenRewrite знайшов порожній beans.xml і не зміг його розпарcити:

org.openrewrite.xml.XmlParsingException: Syntax error in \src\main\webapp\WEB-INF\beans.xml at line 1:0 mismatched input '<EOF>' expecting {COMMENT, UTF_ENCODING_BOM, '<?xml', '<', SPECIAL_OPEN, DTD_OPEN}.
        at org.openrewrite.xml.XmlParser$ForwardingErrorListener.syntaxError(XmlParser.java:118)
        at org.antlr.v4.runtime.ProxyErrorListener.syntaxError(ProxyErrorListener.java:41)


Але це не викликало завершення роботи, потім вискочила ще одна помилка, на жаль, у виключенні нічого не сказано про його причину:

java.lang.StringIndexOutOfBoundsException: Range [1023, 1023) out of bounds for length 1020
        at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
        at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
        at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
        at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
        at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
        at ja-va.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
        at ja-va.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
        at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4608)
        at java.base/java.lang.String.substring(String.java:2720)
        at org.openrewrite.java.isolated.ReloadableJava17ParserVisitor.convert(ReloadableJava17ParserVisitor.java:1480)

Щобільше, при детальному аналізі виявилося, що SBM використовує досить стару версію rewrite-spring — 4.26, хоча вже є версія 4.34. Причина банальна — SBM виходить раз на 2-3 місяці. Можна, звичайно, спробувати вручну пропатчити jar-файл, але це вже на свій страх та ризик. Після закінчення сканування SBM виводити адресу Web UI:

localhost:8080/spring-boot-upgrade

Відкриваємо у браузері посилання і перед нами як опис змін у Spring Boot 3 зі зручною навігацією по секціях:

Так і можливість запустити вручну окремі рецепти (кнопка «Run Recipe)»::

Натискаємо цю чарівну кнопку, яка призводить до виключення в консолі. Спроба розібратися в тому, що відбувається, призвела до цікавих спостережень. Якщо запустити ще раз SBM, то він виведе попередження:

It seems that the project was changed while running the recipe. The project was scanned again but you'll need to run the recipe again.] with root cause
org.springframework.sbm.engine.git.ProjectOutOfSyncException: It seems that the project was changed while running the recipe. The project was scanned again but you'll need to run the recipe again.
        at org.springframework.sbm.engine.git.ProjectSyncVerifier.verifyProjectIsInSyncWhenGitAvailable(ProjectSyncVerifier.java:57)

Звідки SBM знає про рецепти, які вже були запущені? Після роботи SBM з’являється папка .rewrte-cache, де є база RocksDB і складаються логи роботи. Крім того, запуск кожного рецепта автоматично створює Git коміт без змінених файлів, але з текстом, наприклад: SBM: applied recipe ’sbu30-upgrade-dependencies’

Це можна вимкнути, змінивши спеціальну властивість sbm.gitSupportedEnabled (за замовчуванням увімкнено).

Спробуємо запустити Spring Boot Migrator CLI::

java -jar spring-boot-migrator.jar

Тут виявилася ще одна цікава особливість. Якщо скопіювати jar-файл у папку з проєктом, то все працює успішно, якщо ж до будь-якої іншої, то потрібно вказати папку з проєктом командою scan, і чомусь видається дивна помилка, яку ми так і не змогли усунути:

[X] SBM requires a Maven build file. Please provide a minimal pom.xml

Можливо, це було викликано особливостями операційної системи, правами чи ще чимось, але нам не вдалося це виправити.

Висновки

Перехід на Spring Boot 3 — серйозний відповідальний процес, які вимагає автоматичних інструментів, таких, наприклад, як OpenRewrite. Він дозволяє автоматично виконати рутинну роботу, а також покращити якість коду за рахунок застосування «best practices» і тих JDK фітч, які з’явилися після 8-ї версії. Є підтримка і Maven і Gradle. Можна запустити процес міграції в «dry» режимі і проаналізувати (а потім застосувати) список змін з докладними поясненнями.

При цьому потрібно розуміти, що OpenRewrite надає базові можливості міграції, працює тільки з файлами вашого проєкту та очікує на виконання певних конвенцій. Так, наприклад, він може перейменовувати та видаляти властивості з .properties та YAML файлів. Але що, якщо ви використовуєте розподілену конфігурацію і налаштування зберігаються віддалено в Consul/Zookeeper/Github? У такому разі вам доведеться або виконати перейменування вручну або написати свій клас-рецепт.

Spring Boot Migrator розширює можливості OpenRewrite і дозволяє застосувати окремі рецепти, а не всі скопом, як OpenReerite. Крім того, він має два режими роботи — CLI і Web UI.

👍ПодобаєтьсяСподобалось7
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
sbm.gitSupportedEnabled

— як цей проперті виставити??

вже знайшов. Там gitSupportEnabled, а не gitSupportedEnabled

робив руками + деяка допомога від IntelliJ IDEA (Migrate packages and classes)
особливих проблем не було (проект уже був на Java 17), окрім хіба що:
— бардака з різними версіями netty (spring boot 3.0, aws java sdk, mockserver), але це майже перманентно
— javax.annotation.concurrent.Immutable в кількох заюзаних легасі класах

а, і в кількох ендпойнтах роутінг “злетів”, через те що я migration guide читав не уважно:
“As of Spring Framework 6.0, the trailing slash matching configuration option has been deprecated and its default value set to false. This means that previously, the following controller would match both ‘GET /some/greeting’ and ‘GET /some/greeting/’
...
As of this Spring Framework change, ‘GET /some/greeting/’ doesn’t match anymore by default and will result in an HTTP 404 error.”
ну це ми на фронті поправили, коли перечитав гайд і знайшов причину ))

У Spring MVC 6 зустрілася ще одна проблема.
Раніше GET products//1 працювало нормально, а тепер видає 404, так довелося в декількох місцях це підправити.

ми свою міграцію зробили руками, основні проблеми
1. javax -> jakarta
2. новий hibernate 6 — у нас доволі все просто с базою, але змінилася робота с кастомними типами
3. ми довго сиділи на springfox для генерацію swagger — довелось переходити на springdoc-openapi. зайняло час, бо у нас були касмні плагіни
4. spring integration у парі з azure service bus — попив крові
5. ну і перехід на новий azure monitoring agent — довелось викинути деякі костилі и дописати нові.
5.1 передача instumentation key з коду
5.2 вимкнули їх logging (бо його не можливо конфігурувати) та використовуємо кастомний апендер з цензором.

Досить тривалий процес. А чому ви не розглядали автоматизований спосіб оновлення (той самий OpenRewrite)?

ctrl+r javax -> jakarta досить швидко :) це єдине що він пришвидшив би, наскільки я розумію
а насправді, ми не знали про openrewrite.

ще там оновився logback до 1.4.х и тепер не працює кастомізація через

Ми використовуємо Log4j 2.x.

Мені цікаво, як ви змогли «продати» цю міграцію бізнесу?

ми з самого початку раз або два рази на рік апгрейдимся. але у нас хороший CTO, він розуміє навіщо.
spring boot два рази на рік, java version деякий час після LTS
випадок с log4j додав вагомих аргументів для цього.

Що ви маєте на увазі? Продати сам факт міграції чи те, що ми використали автоматизований підхід?

скорей всего имелось ввиду затрата ресурсов не на новые фичи :)

а затрати ресурсів на пожежу з security vulnerability? коли у тебе настільки старий сетап, що навіть патча не буде.
треба на це давити
+ затрати на розробку на застарілих інструментах

вот так и продают подобные технические задачи :)

Можна аргументувати тим, що в останніх версіях Spring/Spring Boot є нові фітчі, які нам знадобляться

Ну дивіться, ви приходите до бізнесу, і пропонуєте міграцію. І зрозуміло з самого початку, що це буде складно і доволі ризиково. При цьому це класичний «рефакторинг», що не додає жодної нової функціональності, і бізнес, якщо він не геть далекий від техніки, це повинен був розуміти. Як ви аргументували необхідність такого апгрейду?

Якщо компанія продуктова, там немає жодного «бізнесу». А на чолі проекту/стартапу найчастіше стоять технічно підковані люди, які краще за вас розуміють необхідність періодичного рефакторингу та upgrade.

Почніть продавати цю ідею поступово:
1. Запропонуйте апгрейд
2. Покладіть у беклог, нехай муляє
3. Нагадуйте коли бачите можливість. Бувають такі часи коли не горить.

Пропонуємо апгрейд:
Гайз наш продукт написаний на spring boot. Вони довго випускали сумісні версії (насправді ніт), а тепер вирішили вступити в нову еру з новою версією, яка не зовсім сумісна зі старими. Нам треба почати думати про міграцію, або залишимося в минулому на фреймворку без підтримки і секьюріті апдейтів (мабуть, це єдине що розуміють усі). Чім довше чекаємо тим складніше/дорожче буде перехід.

Не працюйте в компаніях, де бізнесу доводиться «продавати» ідею оновлення базового ПО.

Підписатись на коментарі