Зураб Белый, руководитель группы, Java-практик, рассказывает свою историю работы над проектом для одной крупной компании и делится накопленным опытом.
Как я зарегистрировался.
Я присоединился к проекту в конце лета 2017 года как обычный разработчик.
Не могу сказать, что на тот момент мне это очень нравилось: технологии в проекте были старые, общение внутри команды было сведено к минимуму, общение с заказчиком было сложным и непродуктивным.
Вот как меня встретил проект. В то время у меня было только одно желание: как можно быстрее выбраться из этого.
Расскажу немного о проекте в целом.
Это официальный портал одной крупной компании с общей информацией, новостями, акциями и другим контентом.
Все маркетинговые рассылки содержат ссылки на определенные страницы сайта, т.е.
нагрузка стабильно средняя, но в определенные моменты времени может достигать высоких значений.
Особого внимания требует стабильность и доступность веб-приложения — каждая минута простоя сервиса приводит к большим потерям для заказчика.
Хижина, покосившаяся от ветра
Поначалу я в основном изучал техническое состояние проекта, исправляя мелкие ошибки и внося мелкие улучшения.С технической точки зрения приложение выглядело ужасно: монолитная архитектура, построенная на устаревшей коммерческой версии dotCMS, код написан на Java версии 6, когда уже давно вышла девятая версия, серверный рендеринг клиентской части на фреймворке Velocity , который к тому времени уже несколько лет не использовался и поддерживался.
Каждый экземпляр запускался в JBoss AS и маршрутизировался с помощью Nginx. Утечки памяти приводили к постоянным перезапускам узлов, а отсутствие нормального кэширования приводило к увеличению нагрузки на серверы.
Но самой большой проблемой стали изменения, внесенные в код CMS. Исключили возможность безболезненного обновления до более свежей версии.
Хорошей иллюстрацией этого стал переход с версии 3.2 на 3.7, который на тот момент как раз заканчивался.
Переход на следующую минорную версию занял больше года.
Ни о каких популярных решениях, таких как Spring Framework, React.js, микросервисная архитектура, Docker и т. д., не упоминалось.
Углубляясь в проект, стали видны последствия такого технического состояния.
Самой серьезной из них была невозможность локального запуска приложения для разработки и отладки.
Вся команда из 8 человек работала на одном стенде разработки, где была развернута копия промышленной версии приложения.
Соответственно, отлаживать свой код мог одновременно только один разработчик, а выкатывание обновленного кода блокировало всю команду.
Апогеем стала провальная распродажа, в ходе которой пользователям по различным каналам были отправлены миллионы писем, СМС и push-уведомлений — одновременно были открыты десятки тысяч сессий.
Серверы не могли справиться с этим, и большую часть времени портал был недоступен.
Приложение плохо масштабировалось.
Сделать это можно было только одним способом: развернуть рядом еще одну копию и сбалансировать нагрузки между ними с помощью Nginx. И каждая доставка кода в производство требовала большого объема ручной работы и занимала несколько часов.
Через полгода после моего участия в проекте, когда ситуация уже начала выходить из-под контроля, было решено кардинально изменить ситуацию.
Процесс перехода начался.
Изменения коснулись всех сфер: состава команды, рабочих процессов, архитектуры и технических составляющих приложения.
Мы строили и строили.
Прежде всего, произошли кадровые изменения.
Несколько разработчиков были заменены, и я стал руководителем группы.
Переход к современным решениям начался с привлечения в команду людей, имевших опыт работы с ними.
Процедурные изменения носили более глобальный характер.
К тому моменту разработка уже велась по методологии Agile+Scrum, двухнедельными спринтами с сдачей в конце каждой итерации.
Но на самом деле это не только не увеличило скорость работы, а наоборот, замедлило ее.
Ежедневные митинги продолжались по полтора-два часа и не дали никаких результатов.
Планирование и подготовка превратились в споры, ругань или простое общение.
С этим нужно было что-то делать.
Изменить что-либо в этом направлении изначально было очень сложно — команда практически потеряла доверие к заказчику, особенно после неудачной продажи.
Каждое изменение нужно было долго обосновывать, обсуждать и доказывать.
Как ни странно, инициатива исходила от заказчика.
Со своей стороны был привлечен Scrum-мастер, который будет контролировать правильность применения подходов и методологий, настраивать процессы и настраивать команду на работу.
Несмотря на то, что его пригласили всего на несколько спринтов, это помогло нам правильно подготовить основу.
Существенно изменился подход к общению с заказчиком.
Мы стали чаще обсуждать проблемы в процессах, ретроспективы стали более продуктивными, разработчики охотнее давали обратную связь, а заказчик со своей стороны шёл навстречу и всячески поддерживал процесс перехода.
Но, положа руку на сердце, скажу честно: было много моментов, когда какие-то изменения внутри коллектива проводились «в секрете», а после появления положительных результатов об этом докладывалось заказчику.
За полгода отношение поменялось на комфортное рабочее общение.
Далее последовало несколько тимбилдингов, одно- и двухдневные встречи всей команды разработчиков с командой заказчика (маркетолог, аналитик, дизайнер, Product Owner, контент-менеджеры и т. д.), совместные походы в бар.
Спустя год и по сей день общение можно назвать дружеским.
Атмосфера стала дружелюбной, непринужденной и комфортной.
Конечно, без конфликтов не обойтись, но даже в самой счастливой семье иногда случаются ссоры.
Не менее интересные изменения произошли за этот период в коде приложения, его архитектуре и используемых решениях.
Если вы не подкованы технически, смело пропускайте весь текст до заключения.
И если вам так же повезло, как мне, добро пожаловать! Весь переход можно разделить на несколько этапов.
Подробнее о каждом.
Шаг 1. Определение критических проблемных областей.
Здесь все было максимально просто и понятно.
Прежде всего нужно было избавиться от зависимости стороннего коммерческого продукта, разрезать монолит и сделать возможной локальную отладку.
Я хотел разделить клиентский и серверный код, разделить его архитектурно и физически.
Еще одна проблемная область – квалификация.
В проекте полностью отсутствовало автоматическое тестирование.
Это несколько усложнило процесс перехода, так как все приходилось проверять вручную.
Учитывая, что технического задания на функционал никогда не было (это связано со спецификой проекта), была большая вероятность что-то упустить.
Очертив проблемные места, мы еще раз просмотрели список.
Это выглядело как план.
Пришло время построить небоскрёб! Шаг 2. Обновление кодовой базы.
Самый длинный этап.
Все началось с перехода на сервисную архитектуру (не путать с микросервисами).
Идея была следующая: разделить приложение на несколько отдельных сервисов, каждый из которых будет выполнять свою определённую задачу.
Сервисы должны были быть не «микро», но и складывать все в один котел тоже не хотелось.
Каждая служба должна была представлять собой приложение Spring Boot, написанное на Java SE 8 и запускаемое на Tomcat. Первым был т.н.
«контент-сервис», который стал прослойкой между будущим приложением и CMS. Оно стало абстракцией на пути к содержанию.
Предполагалось, что все запросы, которые мы ранее делали непосредственно к CMS, будут выполняться через этот сервис, но по протоколу HTTP. Данное решение позволило нам сократить связность и создало возможность в последующем заменить dotCMS на более современный аналог или полностью отказаться от использования коммерческих продуктов и написать собственное решение под наши задачи (забегая вперед скажу, что это именно путь, который мы прошли).
Они сразу создали основу для разделения фронтенд- и бекенд-кода.
Мы создали фронт-сервис, который стал отвечать за распространение кода, написанного на React. Установили npm, настроили ноду и отладили сборку — все так, как и должно быть по современным тенденциям в клиентской части.
В целом функционал на сервис был выделен по следующему алгоритму:
- создал новое приложение Spring Boot со всеми необходимыми зависимостями и настройками;
- портировали всю основную логику (часто писали ее с нуля, обращаясь к старому коду, просто чтобы убедиться, что не забыли ни о каких нюансах), например, для службы кэширования - это возможность добавления в кеш , прочитать его и сделать недействительным;
- весь новый функционал всегда писался с использованием нового сервиса;
- Мы постепенно переписали старые части приложения в новый сервис в порядке важности.
- Контент-сервис.
Выступал в качестве промежуточного слоя между приложением и CMS.
- Кассовое обслуживание.
Простое хранение с использованием Spring Cache.
- Служба АА.
На старте он занимался лишь распространением информации об авторизованном пользователе.
Остальное осталось внутри dotCMS.
- Фронтальный сервис.
Отвечает за распространение клиентского кода.
Учитывая весь опыт проекта, мы решили, что наличие функциональных тестов значительно упрощает жизнь и возможное дальнейшее обновление приложения.
Пришло время внедрить их в проект. Юнит-тесты кода, как это ни печально, отмерли практически сразу.
На их написание и поддержку уходило много времени, а у нас его было очень мало, потому что помимо переписывания кода перед нами стояли текущие задачи по новому функционалу, и часто всплывали ошибки.
Было решено сосредоточиться только на тестировании интерфейса приложения с использованием Selenium. Это, с одной стороны, упростило наше регрессионное тестирование перед поставкой в производство; с другой стороны, стало возможным проводить рефакторинг на стороне сервера, отслеживая при этом состояние на стороне клиента.
В команде не было специалиста по автоматизации, а написание и поддержание актуальности автотестов требует дополнительных затрат. Никого из тестировщиков переобучать не стали, а в команду добавился еще один человек.
«Шаг 4. Автоматизация развертывания.
Теперь, когда у нас есть отдельные сервисы, когда фронтенд отделился от бэкенда, когда основной функционал начал покрываться автотестами, пришло время открыть банку пива и автоматизировать всю ручную работу по развертыванию и поддержке приложения локально, на демо- и производственных серверах.
Разрезание монолита на части и использование Spring Boot открыли для нас новые горизонты.
Разработчики получили возможность отлаживать код локально, запуская только ту часть функционала, которая была для этого необходима.
Тестовые стенды наконец-то стали использовать по назначению — туда попадал более-менее отлаженный код, готовый к начальному и квалификационному тестированию.
Но впереди еще много ручной работы, которая отнимает наше драгоценное время и силы.
Изучив проблему и опробовав варианты решения, мы остановились на связке Gradle + TeamCity. Поскольку сборщиком проекта был Gradle, добавлять что-то новое не было смысла, а написанные скрипты оказались платформо-независимыми, их можно запускать на любой ОС, удаленно или локально.
И это позволило не только использовать любое CI/CD-решение, но и безболезненно сменить платформу на любую другую.
TeamCity был выбран благодаря богатому встроенному функционалу, наличию большого сообщества и длинному списку плагинов на все случаи жизни, а также интеграции со средой разработки Intellij IDEA. На данный момент в CI-системе имеется более 100 скриптов оптимизации и более 300 задач для их запуска с разными параметрами.
Это не только развертывание на тестовые стенды и доставка на производство, но и работа с логами, управление серверами, маршрутизация и просто решения однотипных рутинных задач.
Часть задач сняли с наших плеч.
Контент-менеджеры смогли сами сбросить кеш.
Ребята из техподдержки получили возможность самостоятельно управлять отдельными службами и проводить первичные реанимационные мероприятия.
Сон разработчиков стал глубже и спокойнее.
Шаг 5. Собственная CMS. После того, как нам удалось абстрагироваться от коммерческой CMS, появилась возможность получать данные из другого источника, не переписывая код приложения.
Где брать те или иные данные, решал контент-сервис.
После поиска готовых решений и их анализа мы решили написать собственную систему управления контентом, так как ни одно из них не удовлетворяло наши потребности в полной мере.
Написание собственной CMS — бесконечный процесс; Постоянно появляются новые потребности и желания.
Мы выбрали несколько основных функций и отправились в чудесный мир разработки.
На запуск первой версии в производство у нас ушло полтора месяца.
По мере готовности функционала в новой CMS переносим в нее контент из старой.
Все новые страницы больше не имеют никакого отношения к dotCMS. Со временем это позволило отказаться от платной версии и перейти на версию сообщества, а позже и вовсе отказаться от чего-то стороннего.
Шаг 6. Докеризация.
Засучив штаны, мы начали путешествие в мир хипстерского программирования.
Этот этап для моего проекта стал завершающим в процессе реструктуризации.
Это продолжается и по сей день.
Основной областью, для которой вообще появился этот этап, было масштабирование.
Комбинация Docker+Kubernetes+Consul позволяет не только облегчить развертывание на разных серверах и управлять каждым сервисом отдельно, но и гибко масштабировать все приложение, делая это только в тех местах, где это необходимо, и только до тех пор, пока требуется.
Более подробно смогу описать только тогда, когда мы полностью перейдём на это решение в продакшене.
.
и наконец построил его.
Ура! Прошел год с момента начала обновления приложения.
В настоящее время на Spring Boot написано 26 REST-сервисов.
Каждый из них имеет подробную документацию по API в пользовательском интерфейсе Swagger. Клиентская часть написана на React.js и отделена от серверной части.
Все основные страницы портала покрыты тестами.
Было проведено несколько крупных распродаж.
Переход на современные технологии, избавление от старого кода и стабильная работа серверов очень мотивируют разработчиков.
Из «как сказали, так и делаем» проект перешёл в направление, где каждый заинтересован в успехе и предлагает свои варианты доработок и оптимизации.
Изменилось отношение заказчика к коллективу, создана дружеская атмосфера.
Это результат работы всей команды, каждого разработчика и тестировщика, менеджеров и заказчиков.
Было очень сложно, нервно и порой на грани фола.
Но сплоченность команды, грамотное планирование и осознание результатов позволили нам преодолеть все трудности.
Теги: #java #react.js #scrum #CMS ##reksoft ##reksoft
-
Знакомство С Kohana 3.0. Часть 4.
19 Oct, 24 -
Google Начинает Закупки Технологий
19 Oct, 24 -
Заработные Платы Разработчиков В Армении
19 Oct, 24 -
Почему Вам Следует Использовать Vim
19 Oct, 24