Как стать автором
Обновить

Как избавиться от старого продукта, не удаляя продукт?

Время на прочтение5 мин
Количество просмотров2.9K

image


Привет! Согласитесь, во многих крупных компаниях рано или поздно возникает проблема — какой-то прибыльный продукт превращается в legacy. Причем обычно менеджмент это осознает, когда хочет "просто" поменять одну цифру на другую к вечеру, а разработчики оценивают это в два спринта. Или когда разработчики один за другим теряют мотивацию и покидают продукт, а новые кандидаты выбирают другие офферы.


Часто эту проблему пытаются решить переписыванием продукта с нуля. Но переписывание с нуля кроет в себе отложенную проблему, так как можно потерять мелкие нюансы и в итоге переписанный продукт будет поначалу болеть "детскими болячками", которые много лет назад были вылечены в legacy. Мы, в АльфаСтраховании считаем, что все члены команды разработки должны понимать, что их материальный успех зависит от того, сколько компания зарабатывает, используя написанный ими продукт. А сколько денег принесет продукт, который постоянно спотыкается и плюется ошибками? И клиентов не удовлетворить ответами из серии: "Ну зато у нас тут микросервисы и неблокирующие стримы". Им важно, чтобы продукт работал быстро и стабильно. А написан ли он в виде скрипта на bash или в виде микросервисов на Scala, потребителям наплевать. Конечно, разработчикам не нужно забывать и о своём развитии — регулярно изучать новые технологии, получать опыт использования их в продуктиве, но не в ущерб бизнесу.


Что с этим делать? Мы нашли для себя ответ, успешно применили на одном продукте и надеемся что этот подход или его части помогут и другим.


Примерно в 2014 г., когда у нас в компании вся разработка, по сути, была только в СУБД Oracle (с помощью PL/SQL), у кого-то возникла идея написать продукт на Java. Поставили разработчикам Java и сказали писать на ней. Экспертизы по Java ни у кого не было, архитектуру никто не проектировал, процедурный стиль программирования просто перенесли на Java, а для отрисовки UI разработчики выбрали Vaadin Framework. На продукте разработчики менялись с регулярностью, кто-то пытался переписать отдельные части, кто-то создавал новые классы, соответствующие ООП, но архитектуры по прежнему не было и официально бизнес не давал времени на рефакторинг. Скорость разработки фич была невысокой, в продукте часто появлялись ошибки, они долго находились и исправлялись, а мотивация разработчиков медленно и верно двигалась к 0. Бизнес это стало не устраивать, разработчиков тоже, и в какой-то момент встал вопрос, что делать с продуктом. И, как водится, ответ был такой: "Выкинуть и написать с нуля". Бизнес обеспокоился таким ответом и решил собрать "экспертную группу", чтобы они посмотрели на продукт и решили, что с ним можно сделать с минимальными потерями.


Вначале мы, как экспертная группа, решили конкретизировать все проблемы продукта, затем попробовать их сгруппировать и найти решения для каждой группы. Для этого мы опросили команду разработки, владельца продукта и даже узнали у некоторых кандидатов почему они отказались от офферов. Основные проблемы оказались такие:
image


Посмотрев на эти проблемы стало ясно, что их можно решить с помощью трех вещей: Архитектурного подхода, Шаблонов проектирования и Spring Framework.
image
В качестве архитектурного подхода (архитектурного паттерна) мы выбрали MVC плюс слой сервисов. Да, мы оставили монолит. Думаю не стоит в этой статье еще раз доказывать, что микросервисы это не серебряная пуля, приведу лишь пару доводов, почему все-таки монолит. UI в продукте уже был (Vaadin), он позволял Java разработчикам довольно быстро разрабатывать нормально выглядящий UI, весь функционал хорошо вписывался в одну доменную область, и монолит справлялся с нагрузкой с запасом. Поэтому плюсы монолита (простое и быстрое локальное развертывание и отсутствие внутренних интеграций) перевешивали минусы (горизонтальное масштабирование и масштабирование отдельных сервисов). Конечно эти плюсы монолита были превращены в архитектурные минусы — разработчики с радостью связывали UI компоненты с БД, добавляли какие-то утилитарные методы в классы, отвечающие только за отрисовку UI, вызывали методы одного UI класса из другого, что также привело к сильному запутыванию кода. Но наш архитектурный принцип жестко регламентировал, что так делать нельзя. Взаимодействие должно быть только таким:
image


Сервисы отвечали за бизнес-логику, чтобы обеспечить доступ к бизнес-функционалу и через API, и через пользовательский интерфейс. Т.е. оперировали бизнес-сущностями, а Controller отвечал только за поведение View. Controller включал в себя нужные сервисы и либо получал из них данные для отображения на UI, либо передавал данные по действиям юзера. Также эти же сервисы использовались через API. В дальнейшем можно расширять RestController (он уже был), менять технологию UI и все это без изменения бизнес-логики.


Из шаблонов проектирования основными стали Абстрактная фабрика и Стратегия. Основная суть приложения была дать партнерам возможность продавать наши страховки. Все было одинаково, менялись только объекты страхования и настройки для партнеров. Поэтому Абстрактная фабрика здесь очень подходила. А Стратегию использовали для реализации различных способов оплаты — страховку можно было оплатить разными способами и это влияло как на передаваемые данные, так и на способ обработки ответа от платежных систем. Позже все увидели, что паттерны очень сильно сократили объем дублированного кода и исключили "размазывание" бизнес-логики по нескольким классам.


С внедрением Spring были свои нюансы. В продукте было много самописных singleton, статических полей и методов — делать их всех бинами, а потом еще заменять их внедрение во всех классах за один раз выглядело нелегкой задачей. Мы нашли такое интересное решение, которое нам помогло переводить самописные singleton на Spring бины постепенно. Вот пример, когда у нас уже есть композиция, и мы сделали наш самописный singleton MyBusinessProcess Spring бином:


class SuperService {
  public void doSuperSmth() {
     Service.getInstance().doSmth();
  }
}

class Service {
  …  
  public static void getInstance() {...}

  public void doSmth() {
    //MyBusinessProcess.getInstance().startProcess();
    Context.getBean(MyBusinessProcess.class).startProcess();
  }
}

Как видите, нам нужно было только поменять вызов одного статического метода MyBusinessProcess.getInstance() на другой Context.getBean(MyBusinessProcess.class). Переведя приложение на SpringBoot и проверив этот подход с бинами выяснили, что это рабочий метод и благодаря ему можно проводить архитектурный и стилистический рефакторинг постепенно в спринтах. Мы договорились с бизнесом, что команда сможет тратить 40% спринта на рефакторинг. А чтобы не тыкаться просто по всему проекту, то рефакторить классы, которые затрагиваются в рамках бизнес-задачи. Если какие-то места не получается просто "зацепить" заодно, в рамках бизнес-задачи, договорились их заводить как задачи на рефакторинг в бэклог.


Ну вот, когда все теоретические и экспериментальные этапы были завершены, надо было приступать к непосредственно рефакторингу. В этом продукте не было тестов. Совсем. Поэтому любые изменения были опасны, а покрыть юнит-тестами всю систему, переполненную статическими полями и методами, было задачей не из легких. Хорошо, что в этом продукте был не сильно сложный UI и были REST методы, затрагивающие основные процессы. Перед началом изменений мы написали функциональные тесты на API — после этого можно было уже заниматься рефакторингом, не боясь кардинально что-то сломать.


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


Из нашей истории можно сделать минимум три вывода:


  1. Если решили использовать технологию, позаботьтесь о том, чтобы в команде был хотя бы один профессионал, который имеет в ней опыт
  2. Прежде чем делать систему необходимо спроектировать архитектуру, и если через какое-то время бизнес-логика кардинально меняется, необходимо перепроектировать продукт, давать время на рефакторинг и не копить технический долг
  3. Необходимо давать разработчикам возможность использовать в продукте современные mainstream технологии

На все это аргумент один — это экономически выгодно в среднесрочной и долгосрочной перспективе.


Чтобы не превращать пост в трехтомник, мы опустили многие детали. В комментариях с удовольствием ответим на вопросы и можем что-то раскрыть подробнее. Если у вас есть свои успешные подходы, будем рады услышать.

Теги:
Хабы:
Всего голосов 3: ↑2 и ↓1+2
Комментарии20

Публикации

Информация

Сайт
alfastrah.ru
Дата регистрации
Дата основания
Численность
5 001–10 000 человек
Местоположение
Россия