Як модернізувати Java легасі-код

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Всім привіт! На зв’язку Олексій. Я — Java Fullstack-розробник у компанії Yalantis на проєкті ринкових бірж, опціонів та облігацій. Мій досвід охоплює чимало напрямків Java-розробки, таких як SaaS-платформи, Computer vision, DNA processing, Gaming monetization, Bank activity та багато іншого.

Наразі моя діяльність зосереджена на фінансовій сфері.

Часто фінансові продукти здебільшого пов’язані із застарілим кодом (хоча є і новітні продукти, але не про них мовиться). Мені доводиться працювати з легасі-кодом, що може бути досить складним завданням.

Цей досвід спонукає мене поділитися власними переживаннями та «болем» з вами. Такий код часто вимагає більше часу на розуміння та виправлення, не кажучи вже про внесення будь-яких нововведень. Сподіваюся, моя історія знайде відгук в аудиторії, що стикається з подібними викликами.

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

private List<Market> classicalmambojumbo()
            {
                List<Market> list = new ArrayList<>(activeAssets.keySet());
                Collections.sort(list);

                if (user.getQuoteType() == QuoteType.BATS)
                    {
                        Set<Market> needed = new HashSet<>();
                        needed.add(Market.BATS);
                        needed.add(Market.CANADA);
                        needed.add(Market.OPRA);

                        list.removeIf(m -> !needed.contains(m));
                    }
                else if (user.getQuoteType() == QuoteType.CBOE1)
                    {
                        Set<Market> needed = new HashSet<>();
                        needed.add(Market.CBOE1);
                        needed.add(Market.CANADA);
                        needed.add(Market.OPRA);

                        list.removeIf(m -> !needed.contains(m));
                    }
                else
                    list.removeAll(Arrays.asList(Market.BATS, Market.CBOE1));

                return list;
            }

У фрагменту коду представлені складнощі роботи з колекціями та умовною логікою, що вимагає видалення або фільтрації даних залежно від певних умов.

Нам потрібно відфільтрувати дані залежно від заданих критеріїв, що збільшує ризик помилок і вимагає глибокого занурення в деталі. Це підкреслює важливість обережного підходу під час роботи з таким кодом, щоб уникнути проблем.

І якщо тут плюс-мінус дещо можливо зрозуміти (за винятком назви, звісно), наступний метод викликає моторошність:

  
protected void printHistoryForBilling(User user){
        Map<Market, List<Exchange>> map =  user.getExchangeMapping();
        List<Exchange> agrMap = map.get(market);

        int size = agrMap.size();
        if(size == 0 ){

        }else if(size>1){
            //find the recent contract based on pro and no npro status
            Exchange latestNonProAgr = null;
            Exchange latestProAgr= null;
            for(Exchange agr: agrMap)
            {
                //Ignore deleted contracts in billing history
                if(agr.getStatus().equalsIgnoreCase("D")){
                    continue;
                }

                if(agr.getIsProfessional()){
                    if(latestProAgr == null){
                        latestProAgr = agr;
                    }
                    //logic
                    if(agr.getActivatedOn().getTime() > latestProAgr.getActivatedOn().getTime()){
                        latestProAgr = agr;
                    }
                }else{
                    if(latestNonProAgr == null){
                        latestNonProAgr = agr;
                    }
                    //logic
                    if(agr.getActivatedOn().getTime() > latestNonProAgr.getActivatedOn().getTime()){
                        latestNonProAgr = agr;
                    }
                }

            }
            if(latestNonProAgr!=null) {
                printContractReport(latestNonProAgr, user);
            }
            if(latestProAgr!=null){
                printContractReport(latestProAgr, user);
            }

        }else {
            if(!agrMap.get(0).getStatus().equalsIgnoreCase("D")){
                printContractReport(agrMap.get(0), user);
            }
        }

    }

Річ у тім, що пустий блок,

 if(size == 0 ){

  }

викликає питання, як мінімум, чому він пустий і чому немає логування всередині для подальшого відстежування таких випадків?

Далі, у разі використання Java новіших версій, можливо, було б змінити цикл for(Exchange agr: agrMap) на agrMap.stream, хоча, кому як зручно.

Але найбільшу складність викликає багатошаровість if -> else, і тому потрібно глибоко зануритись та чітко зрозуміти саму бізнес-логіку.

Рефакторинг цих функцій не буду розкривати у статті, дещо згодом наведу інші приклади, але розгляньмо загальні підходи до модернізації Java легасі-коду та приклади успішних реалізацій.

У світі інформаційних технологій легасі-код визначається як програмний код, який був розроблений десятиліття тому і залишається активним, незважаючи на зміни в технологіях та вимогах до ПЗ. Часто це стає викликом для компаній, оскільки старий код може ускладнювати розвиток, утримання та масштабування систем.

Чому ж таки потрібна модернізація легасі-коду або важливість його модернізації?

Однією з основних причин модернізації легасі-коду є забезпечення сумісності з останніми технологічними та безпековими стандартами. Старі версії програмного забезпечення можуть бути вразливими перед новими кіберзагрозами й не використовувати переваги нових технологій.

Модернізація дозволяє впроваджувати сучасні підходи до розробки, використовувати нові бібліотеки та інструменти для покращення ефективності та швидкодії системи. Ще однією важливою причиною модернізації є поліпшення масштабованості та обслуговуваності системи.

Легасі-код може бути монолітним та важко розширюваним, що ускладнює внесення змін та вдосконалення. Застосування модульного підходу, мікросервісної архітектури та інших сучасних принципів допомагає робити систему більш гнучкою та легше розширювальною.

Етапи модернізації легасі-коду

Аналіз коду

Першим етапом є докладний аудит і аналіз наявного коду. Це допомагає з’ясувати його стан, визначити слабкі місця та стратегію модернізації. Тут на допомогу можуть прийти статичні аналізатори коду, такі як SonarQube, Sonar lint які виявляють потенційні баги або вразливі місця, що жили роками.

На мою думку, найбільше в розумінні коду зможе допомогти побудова функціональних та флоу діаграм. Саме після складання такого роду діаграм можна чітко зрозуміти не тільки те, як влаштований код всередині, але й саму бізнес-логіку.

Й ось тоді можливо визначити, які частини коду потребують негайної уваги під час аудиту.

Реінжиніринг

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

Критичні, важливі, середні, мінорні та скласти план правок за категоріями.

Не слід забувати й про впровадження стилю коду, бо це значно полегшить його сприйняття. Як правило складають загальний файл формату xml, де й будуть прописані всі правила форматування та задані відступи, який надалі буде спільно використовуватись розробниками в середовищах розробки.

У випадку складних оптимізацій або перероблень, саме бізнес має вирішувати, наскільки критично та важливо переробити ту чи іншу частину функціонала з нуля, чи додати мінімально необхідні правки. Завдання розробника саме вказати на проблемне місце та запропонувати варіанти рішень.

Тестування

Після внесення змін важливо провести інтенсивне тестування. Це дозволяє виявити можливі проблеми та переконатися, що новий код працює стабільно та ефективно.

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

Важливим аспектом модернізації є також залучення команди розробників, яка має розуміння як наявної системи, так і нових технологій. Співпраця та обмін знанням допомагають забезпечити успішне завершення проєкту. Покриття тестами теж внесе вагомий вплив, особливо за наявності quality gates.

Документація

Під час модернізації легасі-коду важливо створювати та оновлювати документацію. Це сприяє розумінню нового коду та полегшує подальшу підтримку продукту.

Дуже часто в якості інструментів для введення та збереження документації, використовують Google Docs, Drive. Проте існує чудовий Google-плагін під назвою Scribe, який генерує покрокову інструкцію, просто записуючи виконані кроки додаючи скриншоти та описи. Як на мене, це дуже зручно, оскільки зберігає багато часу для написання документації.

Підходи до Модернізації

Рефакторинг коду

Рефакторинг є одним з основних методів модернізації легасі-коду. Цей процес містить зміну структури коду без зміни його зовнішньої функціональності. Використання сучасних підходів до проєктування, поділ коду на модулі та впровадження патернів програмування дозволяють покращити читабельність та обслуговуваність коду. Проте інколи такого роду рефакторинг може зламати наявний функціонал,

тому потрібно бути вкрай обережним під час перебудови чинного коду, застосовуючи покриття тестами.

Ось тут дуже важливий етап — ефективне код-рев’ю. І забезпечити цю ефективність допоможуть механізми (GitLab, GitHub servers) для створення так званих Pull request або Merge request.

Тепер розглянемо приклад оптимізації та рефакторингу наступного методу:

        @POST
        @Path("/terminate")
        @Consumes(MediaType.APPLICATION_JSON)
        public BasicResponse terminate(@Context Context context,
            TerminationRequest request) {
            ....
            ....
            // Terminate all positions
            if (request.isRemoveAllPositions()) {
                try {
                    UserPortfolio manager = new UserPortfolio(dbs);
                    List<MarketSettings> markets = new Util(dbs).getMarketSettings();
                    MarketSettings primaryMarket = new Util(dbs).getPrimaryMarket();
                    for (Account account : accounts) {
                        PortfolioList portfolio = manager.loadPositions(account.getAccountId());
                        portfolio.loadQuotes(proxyClient);
                        for (PortfolioEntry entry : portfolio) {
                            Quote quote = entry.getQuote();
                            MarketSettings market = getMarketSettings(markets, primaryMarket, quote);

                            List<String> errors = uac.credit(account.getAccountNumber(), quote, "REM",
                                "Terminate account with all positions", entry.getQuantity(),
                                quote.getLastPrice(), entry.getPosType(), 0, bouser.getUsername(),
                                market, quote.getLastPrice(), -1, null, IdentifierType.ExchangeSymbol);
                            if (errors != null && errors.size() > 0) {
                                return BasicResponse.Failure()
                                    .setContent(errors.stream().collect(Collectors.joining("\n")));
                            }
                        }
                    }
                } catch (Exception e) {
                    Logger.error(e.getMessage(), e);
                    return BasicResponse.Failure().setContent(e.getMessage());
                }
            }
            // Terminate given user and accounts
            String note = String.format("Initiated by %s - (%s)", bouser.getUsername(), request.getNote());
            if (control.terminateUserAndAccounts(bouser, request.getUsername(), note)) {
                return BasicResponse.Success();
            }

            return BasicResponse.Failure().setContent("Termination failed");
            }

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

По суті, вся робота зводилась у завантаження всіх облікових записів для даного користувача, далі для кожного такого запису завантажувався фінансовий портфель і викликалась збережена процедура з видалення всіх позицій з портфеля. Свого роду очищення облікового запису.

Тут можна спостерігати циклічну вкладеність, а основною складністю є виклик дуже громіздкої збереженої процедури всередині методу credit.

Ще додатковим ускладненням був виклик метода portfolio.loadQuotes(proxyClient) в середині першого циклу, бо це було звернення на сторонній сервіс. На перший погляд, для оптимізації можна було б створити довідник Map<Account, PortfolioList> і вже потім, через ітерацію, викликати метод credit. Проте основною вимогою було виконати всі дії в рамках однієї транзакції, тобто «все або нічого».

Як варіант, рішення можна було б зробити приблизно так:

try (Connection con = source.getConnection())
{
  con.setAutoCommit(false);
  //iterate over map and invoke credit method

   con.commit();
}
catch(Exception e)
{
   con.rollback();
}
finally
{
   con.setAutoCommit(true);
}

Проте доцільніше зробити окрему збережену процедуру, яка б завантажувала всі необхідні дані (як виявилось, всі вони були у базі) на основі вхідних параметрів, і вже потім, у разі помилки на будь-якому етапі, транзакція відкотилася б.

Скажімо так:

CREATE PROCEDURE [dbo].[sp_TotalCredit]
    @UserId AS INTEGER,
    @RemoveAllPositions AS TINYINT = 0
AS

DECLARE @Err AS INT = 0
DECLARE @Msg AS VARCHAR(255) = 'Success'

BEGIN TRANSACTION
IF (@RemoveAllPositions = 1) BEGIN
//logic here 
IF @@ERROR <> 0 BEGIN
    SET @Err = 100
    SET @Msg = 'Failed to complete operation'
    GOTO exit_call
END
END
exit_call:
    IF @@TRANCOUNT > 0 BEGIN
        IF @Err = 0
            COMMIT TRANSACTION
        ELSE
            ROLLBACK TRANSACTION
    END

    SELECT @Err AS Err, @Msg AS Msg
GO

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

Ось так він почав виглядати після:

        @POST
        @Path("/terminate")
        @Consumes(MediaType.APPLICATION_JSON)
        public BasicResponse terminate(@Context Context context,
            TerminationRequest request) {
            ....
            ....
            String note = String.format("Initiated by %s - (%s)", bouser.getUsername(), request.getNote());
            if (control.terminateUserAndAccounts(bouser, request.getUsername(), note, request.isRemoveAllPositions())) {
                return BasicResponse.Success();
            }

            return BasicResponse.Failure().setContent("Termination failed");
        }

Помітна значна різниця, а більшість логіки просто перенесена в збережену процедуру.

Впровадження Unit-тестування та за можливості Integration-тестування

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

Варто згадати Test-Driven Development та Behaviour-Driven Development через те, що ці підходи розробки дозволяють глибше зрозуміти бізнес-логіку, особливо на етапі модернізації коду. Тому потрібно не нехтувати впровадженням розробки через написання тестів. TDD або BDD — вибір за вами!

Наведу приклад недосконалого тесту:

        @Test
        public void test010_ServerTime() throws  IOException
            {
                WatchmenService service = getService(); // ініціалізація сервісу
                ServerTime time = service.getTime();
                Assert.assertNotNull(time);

                System.out.println("Time: " + time.getTime());
                System.out.println("Timezone: " + time.getTimezone());
                Assert.assertNotNull(time.getTime());
                Assert.assertNotNull(time.getTimezone());
            }

У цьому прикладі проста перевірка на null не забезпечить повної гарантії правильності методу, бажано додати перевірку відповідності формату часу, а також відповідність часової зони.

Покращити його можна так:

class FormattedDateMatcher implements DateMatcher {

    private static Pattern DATE_PATTERN = Pattern.compile(
      "^\\d{4}-\\d{2}-\\d{2}$");

    @Override
    public boolean matches(String date) {
        return DATE_PATTERN.matcher(date).matches();
    }
}

        @Test
        public void test010_ServerTime() throws  IOException
            {	
               DateMatcher dateMatcher = getDateMatcher();
                WatchmenService service = getService(); // ініціалізація сервісу
                ServerTime serviceTime = service.getTime();
                Assert.assertNotNull(serviceTime);

                System.out.println("Time: " + serviceTime.getTime());
                System.out.println("Timezone: " + serviceTime.getTimezone());
                Assert.assertNotNull(serviceTime.getTime());
                Assert.assertNotNull(serviceTime.getTimezone());
	    Assert.assertEquals(serviceTime.getTimezone(), TimeZone.getTimeZone(“Europe/Kyiv”))
	    Assert.assertTrue(dateMatcher.matches(serviceTime.getTime))
            }

Особливого значення хотілося б приділити CI/CD-процесам. Вибір засобу забезпечення CI/CD (Gitlab, Jenkins, TeamCity), мабуть, не так критично важливий (за винятком масштабу продукту та фінансових можливостей), як цільова функція неперервної інтеграції змін та автоматичного тестування, особливо залучення цих систем для забезпечення регресивного тестування у процесі рефакторингу та модернізації легасі-коду.

Використання сучасних версій Java. Переваги

Оновлення версії Java є ефективним способом отримати доступ до нових функцій та покращень без значних змін у вихідному коді. Зазвичай під час переходу на нові версії Java розробники можуть використовувати нові API, удосконалені механізми управління пам’яттю та інші покращення.

Перехід з Java 8 або Java 11 на Java 17 або 21 має кілька плюсів. Ось деякі з можливих плюсів під час міграції програмного застосунку:

a) Поліпшення продуктивності:

Нові версії Java часто мають оптимізації та поліпшення продуктивності. Ось декілька цифр зі статистики відповідно до звіту — Java 17 на 8,66% швидше (при задіяному G1 збирачу сміття) аніж Java 11. Також швидше ніж попередні версії за паралельного збирача сміття на 6,54%. Водночас паралельний збирач на 6,39 % швидший за G1.

b) Нові функції мови та API:

Для прикладу, приблизно так би виглядав звичайний клас користувача, написаний до 17 версії Java:

 
public class User {

private String name;
private String login;
private int age;
private String phone;
public String getName() {
   return this.name;
   }
   public void setName(String name) {
   this.name = name;
   }
   public String getLogin() {
   return this.login;
   }
   public void setLogin(String login) {
   this.login = login;
   }
   public int getAge() {
   return this.age;
   }
   public void setAge(int age) {
   this.age = age;
   }
   public String getPhone() {
   return this.phone;
   }
   public void setPhone(String phone) {
   this.phone = phone;
   }
   @Override
   public boolean equals(Object o) {
       if(this== o) return true;
       if(!(o instanceof User user)) return false;
       return this.age== user.age && Objects.equals(this.name, user.name)
          && Objects.equals(this.login, user.login) && 
        Objects.equals(this.phone, user.phone);
       }
   @Override
   public int hashCode() {
   return Objects.hash(this.name, this.login, this.age, phone);
   } 
   @Override 
   public String toString() { 
   return "User{" + "name='" + name + ‘\'’ + ", login='" + login +’\'' + ", age=" + age + ",phone='" +  phone +’\'’ +'};
}
}

А так, якби використовували Java 17:

public record User(String name, String login, int age, String phone) {
}

Різниця відчутна.

Зараз багато хто б сказав, що використовує на всю бібліотеку lombok, проте у випадку record — це вже внутрішні можливості Java 17. Але насправді список таких можливостей можна продовжити — запечатані класи, покращене опрацювання NullPointerException, матчинг патерну instance of й switch та чимало іншого.

c) Покращена безпека:

Нові версії зазвичай містять оновлення безпеки, що дозволяє захищати ваш код від потенційних загроз та вразливостей.

d) Перехід на нову версію може містити оновлення вбудованих бібліотек та фреймворків, що може полегшити використання нових можливостей та покращити роботу вашого застосунку.

e) Підтримка нових технологій:

Нові версії можуть підтримувати сучасні технології та стандарти, такі як вебсервіси, обробка JSON, робота з базами даних, що дозволяє використовувати останні досягнення в розробці.

Хотілося б зазначити таке важливе нововведення: у Java 12 ввели тести мікробенчмаркінгу, щоб продуктивність JVM легко тестувалася за допомогою вже наявних тестів.

Це було б дуже корисно для всіх, хто хоче працювати над JVM. Тести, що додаються, створюються з використанням Java Microbenchmark Harness (JMH). Ці тести дозволяють проводити безперервне тестування продуктивності JVM.

f) Підтримка довших термінів:

Однією з переваг переходу на нові версії є те, що вони зазвичай мають підтримку на довший термін, що дозволяє тривалий час використовувати стабільну та безпечну версію.

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

Використання сучасних фреймворків та бібліотек

Сучасні фреймворки та бібліотеки можуть значно полегшити розробку та підтримку коду. Наприклад, використання Spring Framework дозволяє реалізувати інверсію управління, а Hibernate спрощує взаємодію з базою даних (хоча я прихильник бібліотеки MyBatis)

Використання мікросервісної архітектури

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

curl -x 'POST' \
https://host/notification/send'
-H 'Content-Type: application/jison'
-d '{
"requestId":"1234512075481”
"eventTime": “2024-01-17T12:00:00Z",
"system": "MY_SYSTEM"
"eventCode": " MESSAGE_SENDING
"userId": "123456"
"contacts": {
"phoneNumber": "0689765454"
},
"channel": "sms"
"params": [
{
"info_id": "ID"}, {“text”:”Greetings!”}]

Відразу можна виділити декілька сервісів: UserService, SubscriptionService, TemplateAndMessagingService, NotificationService, HistoryService.

UserService буде перевіряти наявність користувача і передавати дані до Subscription Service, який, своєю чергою, перевірятиме наявність відповідної підписки цього користувача і вже потім звертатись до TemplateAndMessagingService, щоб перевірити чи зареєстровані повідомлення (ми ж не хочемо відправляти що заманеться).

Далі, запит перенаправляється до NotificationService який і відправить повідомлення через різні канали провайдерів (sms, viber message, push notification) та зберігати статус відправки до HistoryService. Приблизно так можна запланувати розділення на мікросервіси, але це доволі поверхово.

Приклади успішних застосувань

Netflix — перехід до мікросервісів.

Netflix, великий провайдер потокового відео, успішно модернізував свій легасі-код, перейшовши до мікросервісної архітектури. Це дозволило їм забезпечити вищу доступність, швидшу розробку та масштабування.

Twitter — використання фреймворку Scala.

Twitter використовує фреймворк Scala для модернізації свого легасі-коду. Scala надає можливості функціонального та об’єктноорієнтованого програмування, що полегшує розробку та збільшує продуктивність розробників.

Amazon — використання контейнеризації та Kubernetes.

Amazon використовує контейнеризацію (зокрема Docker) та оркестрацію контейнерів (Kubernetes) для модернізації своїх додатків. Це дозволяє ефективно керувати й масштабувати додатки в хмарному середовищі.

Висновок

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

Використання рефакторингу, оновлення версій Java, впровадження тестування та інші стратегії можуть значно полегшити цей процес. Приклади успішних ініціатив таких компаній, як Netflix, Twitter та Amazon, свідчать про ефективність правильно обраної стратегії модернізації.

Однак, незважаючи на ці труднощі, модернізація легасі-коду є необхідним етапом для забезпечення стабільності ІТ бізнес-продуктів.

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному2
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

можна просто на котлін переписати, що зменшить кількість коду...

Це тільки якщо замовник одобрить таке рішення)

Автору бы прочитать для начала «Чистый код» и «Идеальный программист», после можно вернуться к творчеству

«Чистый код» читав, а от «Идеальный программист» — ще ні, візьму на замітку)

про чистий кодер (Идеальный программист) можно глянути у мене огляд на книжку тутай youtu.be/zwSH3VqEXwI :)

Змішали до купи все, що могли.
Наявність або відсутність Spring Framework не робить код legacy. Як і Hibernate.
Ви можете застосовувати Spring, але при цьому використовувати XML-декларації бінів, і це робить ваш проект legacy-style.

Як модернізувати Java легасі-код
Використання сучасних фреймворків та бібліотек

Сучасні фреймворки та бібліотеки можуть значно полегшити розробку та підтримку коду. Наприклад, використання Spring Framework дозволяє реалізувати інверсію управління, а Hibernate спрощує взаємодію з базою даних (хоча я прихильник бібліотеки MyBatis)

Цей приклад некоректний, тому що в Java records всі поля final, а значить для не може бути сеттерів і така конвертація неможлива.

public class User {

private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}

А так, якби використовували Java 17:

public record User(String name, String login, int age, String phone) {
}

Так, правильно, там немає бути сеттерів. Дякую за корекцію!

Як модернізувати Java легасі-код

Переписати на С# 10 та .Net Core 8 :-))))

.Net Core 8 :-))))

Который 10 ноября 2026 года превратиться в легаси)

Та ти сам до цього моменту вже в мамонта деградуєш :-) ліл)

> Java 17 на 8,66% швидше (при задіяному G1 збирачу сміття) аніж Java 11.

Як казав мені колись один начальник — все, що не є прискоренням в 2 рази або більше, не цікавить, бо не окупиться.

> Підтримка нових технологій:

Приходиш такий хочеш нових технологій, а у тебе формування одних X.12 з інших.

> Відразу можна виділити декілька сервісів: UserService, SubscriptionService, TemplateAndMessagingService, NotificationService, HistoryService.

І JobSecurityService який всім цим керує.

Як казав мені колись один начальник — все, що не є прискоренням в 2 рази або більше, не цікавить, бо не окупиться.

може бути прискореною саме розробка і то всі етапи як то тестування деплой і все інше

там проблема уже трохи цікавіша бо серверів на +8.66% таки простіше просто докупити а от з програмістами всьо не так однозначно і вигода певно є

Проте доцільніше зробити окрему збережену процедуру

Вітаю, ви своїм рефакторінгом суттєво погіршили стан справ.

Тому що:

1) SQL (PL, T і т.д) у якості саме мови опису логіки повна вирвіглазна начитабельна ***нянабагато гірша ніж будь-яка інша нормальна мова. Синтаксис логічних конструкцій пiзда який уойбіщдуже поганий. Елементарні коллекції (мапа, ліст, сет) які існують для обробки логіки просто відсутні і їх замінюють піздопляскаминеоптимальними рішеннями типу тимчасових селектів. Можна довго продовжувати.

2) в SQL код не завезли від народження нормального версіонування і керування кодом. Не існує імпорту коду, через що будь-яка логіка складніша за інсерт і апдейт починає смердіти ще до кінця написання хуйніпроцедури. Не існує репозиторіїв в гіті, не існує ніхуя крім перекомпіляції ораклового пакета прямо по живому, і похєру ваще що куски іншого коду можуть роз***тись к хєрам — компайл чеку немає. Збільшили в процідуркє кількість аргументів на 1 щоб передать який всратий булеан флажок і перестворили процідуру — всій іншій логіці пизда.

3) через п.1 будь-яка логіка написана в БД викликає бажання піти й вбити автора, а через п.2 система завжди б***ь в нестабільному стані бо кожен їблан з кредами може залізти в БД і переписать вашу процідуру прямо б***ь в рантаймі. Контроля версій доречі немає тому *** там а не гіт блейм, *** там а не відкатить назад.

4) пп.1-3 вже достатньо в принципі щоб забути про існування сторед процедур, але на додачу це ще й загалом хуйова ідея, взяти один домен логіки і распідарасить його сорс код к ***м на різні блд мови які існують в різних кусках інфраструктури.

Я короче ще довго можу продовжувать але мені впадло. Приводити приклади з життя, аналізувати, складати списки причин, і т.д. Впадло. Сторед процедури і винесення логіки в БД це зло і воно має бути забуте а за пропозицію винести шось в БД треба знижувать зарплату і тайтл.

А ще зазвичай фіг віддебажиш в нормальному дебагері і фіг відтестиш логіку юніттестами...

Це також, це я просто сховав під катом «впадло розписувать всі мінуси».
Їх (мінусів) насправді десятки. Там тупо все хyйoво з цим підходом. Зaїбaтись можна якщо все перераховувать. Книжку блть можна написать.

I feel your pain :). В мене був період в житті, коли я часто взаємодіяв з британськими оракловими «бородатими ДБА в светрах» :) Було декілька «цікавих історій», може колись зроблю допис з веселими байками, які зрозумілі тільки програмістам.

Особливо хyйoво це ще коли з БД намагаються зробити одночасно вебсерсвіс + апі + логіка в процедурах. Це просто тотальна пизда.

Це геніально. Показав колегам ваш коментар. Їм сподобалось

IMHO, сам по собі підхід (бізнес-логіка в сп-шках) іноді має право на життя.
А от менеджмент транзакцій всередині це вже... скажімо так, я б таке не робив.

Не має блть він права на життя. Ніякого.
Це блть всратий пережиток минулого і вологі мрії старперів які пам’ятають перші версії оракла і як ще хєр стояв

пов’язані із застарілим кодом

Если на это жалуются пользователи или работодатель, то нема питань, на это будет выделено время, и работа будет оплачена.
В противном случае, Вы нарушаете первый закон совка — инициатива не оплачивается и наказуема

Такие активности обычно, когда программерам нечего делать и они пытаются оправдать свою зарплату. Рефакторинг обучно делается когда тяжело саппортить код (это проблемы программеров и они сами должны об этом заботиться) или когда жалуются пользователи (тогда время будет выделено).

Рефакторинг обучно делается когда тяжело саппортить код (это проблемы программеров и они сами должны об этом заботиться)

Фигня в том, что если не выделяется — постоянно и на основе административного решения — хотя бы 10% времени на рефакторинг и прочие зачистки, то потом никто вам не выделит времени на то, что мол тяжело саппортить код.

Верно. Поэтому при планировании скоупа на спринт на тиму лид или кто главный должен закладывать 10-15 процентов на рефакторинг

тиму лид или кто главный должен закладывать 10-15 процентов на рефакторинг

если позволяют косты и время) роскошь это

это так не работает за это всегда спросят «а это что у тебя за косты? а кто тебе эти косты заапрувил с финансового?»

максимум там что возможно на тим лида это «двойная бухгалтерия» с тем чтобы продвигать такой сякой рефакторинг в каждой задаче фиче фиксе и прочих

... но обычно таким ооочень редко занимаются потому что умеючи надо и тут начинается «конфликт интересов» где «умеючи надо» это значит «ты самый умный в комнате» это значит «это не твоя комната» и «умеючи надо» просто переходит в другое место на +20% и занимается ровно тем же ж уже там или там делает не тим лида а уже лида направления и далее уже ближе к уровню «решать за косты»

... и так по циклу ))

youtu.be/kfHrnwYAnjc?t=34

Никто с лида команды не спрашивает за косты. Лид вообще обычно не ведает кто сколько в тиме получает (в Украине так)

чуловеко часовые косты

зато с него за дедлайны и тикеты могут спросить, что так или иначе — косты

Приклади успішних застосувань

От тільки не зрозумів, до чого у специфічрій статті про Java приклади про мікросервіси, Scala та Docker. Це ортогональні речі

Це ті рідкісні випадки коли модернізуючи код зачіпляш інші технології

Зараз багато хто б сказав, що використовує на всю бібліотеку lombok,

Holywar mode on
Нещадно борюся з цим лайном на усіх проектах де задіяний
Holywar mode off

Тому що мені дуже не подобається, коли згенерований байт-код не відповідає сорс-коду — це персональне. Тому що ми намагаємось бути HITRUST-сertified компанією незабаром, і останнє що мені потрібно — це відповідати на потеційні пов’язані запитання впродовж аудіту — це професійне.

Солюшин архітект опускається аж до такого?

Уявіть собі )) - не тільки різнокольорові прямокутники та стрілочки.
Але якщо серйозно — стек розробки має бути суворо уніфікований, і слідкувати за цим потрібно постійно.

Я так розумію, в результаті розробники були в захопленні від свого архітекта ))
P.S.
В переважній більшості випадків цілком корисна ліба.
Бачив тільки один маленький проект, де розробник, схоже, вирішив використати можливості lombok ліби «на повну», включаючи купу експериментальних фіч. Код виглядав «феєрично» ))

Я так розумію, в результаті розробники були в захопленні від свого архітекта ))

Мене це не бентежить. Якщо комусь з розробників раптово стає «проблематично працювати з багатослівним кодом», тому що у класі присутні 5 пар get/set, він мені не потрібен у команді

Андроїд розробники на Котлін впіймали інфаркт від передозування Джава легасі в крові 😂
Було цікаво почитати, дякую)

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