Легаси система что это
Что такое легаси в коде
Однажды, Симба, всё это будет твоим
Иногда программисты на вопрос, почему программа работает именно так, отвечают, что это «легаси» и исправить ничего нельзя. Разберёмся, что это значит, насколько это мешает разработке и что делают с легаси-кодом.
Что такое легаси
С английского legacy переводится как «наследие». Легаси-код — это код, который перешёл «по наследству» от предыдущих разработчиков. Чаще всего это происходит так:
👉 Проще говоря, легаси — это код, про который говорят: «Это ещё Михалыч писал 8 лет назад для синхронизации с сервером, он работает, мы его не трогаем, потому что иначе всё сломается». При этом Михалыча в компании давно нет, документации тоже нет, и проще этот код не трогать совсем.
Так как легаси — это старый код, то обычно на него завязаны многие важные вещи в программе. Получается замкнутый круг: отказаться от легаси нельзя, потому что без него всё сломается, но и поддерживать его в рабочем состоянии тоже сложно, потому что никто не хочет разбираться в старом коде.
Откуда берётся легаси
Причин появления легаси может быть несколько:
Легаси — это не какое-то преступление, а часть жизни любой живой ИТ-компании. Рано или поздно у любого продукта появится легаси. И чем крупнее проект, тем больше его будет. Например, в исходном коде Windows 10 до сих пор остаются фрагменты кода, написанные ещё 20 лет назад для Windows 3.1.
Легаси — это плохо?
Легаси — это просто старый код, который нужно поддерживать наравне с новым. Если он работает — отлично, пусть живёт. Другое дело, что команде, например, было бы удобнее, чтобы код был написан не на старом фреймворке, а на новом, который знают все.
Получается, главный минус легаси-кода не в том, что он работает плохо, а в том, что его неудобно поддерживать.
Что значит «поддерживать старый код»?
Например, в старом коде для запроса к серверу идёт сначала адрес, а потом номер запроса. Спустя 10 лет требования сервера изменились, поэтому сначала должен идти запрос, а потом уже адрес. Значит, нужно изменить порядок полей в коде.
Если старый код понятен и хорошо задокументирован, на эту задачу уйдёт две минуты. Если это старые пыльные легаси-кишки, то это может стать задачей на час.
Что делать с легаси-кодом
Если легаси-код работает и не требует вмешательства и поддержки — то можно пока ничего не делать, пусть работает. Будет время — перепишем на новый фреймворк, а если нет, то и так пока поработает.
А если нужно срочное вмешательство — пахнет бедой. Зовите менеджеров.
Код без тестов — легаси
Если вы работаете в IT, то о легаси вы слышите часто — обычно с множеством негативных коннотаций. Понятно, что это не «хороший код», но какой? Может старый, может не поддерживаемый или не обновляемый, а может просто чужой? Есть ли «полноценное» определение «легаси», на которое можно ссылаться? А когда разберемся — что нам делать с легаси? Попробуем разобраться. Спойлер: выводы неочевидны.
Автор — Николас Карло, веб-разработчик в Busbud (Монреаль, Канада). Специализируется на легаси. В свободное время организует митап Software Crafters и помогает с конференциями SoCraTes Canada и The Legacy of SoCraTes.
Данная статья была скомпилирована (и отредактирована) из двух статей Николаса: «What is Legacy Code? Is it code without tests?» и «The key points of Working Effectively with Legacy Code». Показалось логичным рассказать о том, что такое легаси, а потом — как с ним работать.
Что такое «легаси»?
Возможно, если вы задавались этим вопросом, то встречали определение от Майкла Физерса. Майкл выпустил книгу «Working Effectively with Legacy Code» в 2004 году, но она до сих пор актуальна. Комикс это отлично иллюстрирует.
В своей книге Майкл пишет своё определение:
«Для меня легаси — это просто код без тестов».
Почему Физерс так считает? Потому что по его многолетнему опыту без тестов обычно трудно узнать всё, что код умеет. Если тестов нет, то для понимания, что код делает, вам нужно внимательно его прочитать, воспроизвести программу в своей голове и представить все возможные сценарии. Потом вы поменяете код и нужно снова представить все сценарии. Или проверить их вручную, но всегда есть шанс что-то сломать.
Это хорошее определение: чаще всего тесты отсутствуют, так что это хорошее начало. Но это ещё не всё — есть нюансы.
Код с тестами также может быть легаси. Если вы читаете тесты, но не можете понять, что должен делать код — они отстой. Плохие тесты только мешают: тестируемый код так же трудно отрефакторить, как если бы у него не было тестов, а может даже и сложнее!
Тестов может и не быть, но код всё ещё легко можно отрефакторить. Возможно, вы поддерживаете небольшую кодовую базу без тестов, которую легко понять и рефакторить. Хотя, по моему опыту, это аномалия. Эту кодовую базу можно было бы проверить, но отсутствие автоматизированных тестов всё же не позволяет квалифицировать его как легаси.
Перейдём к моему определению легаси.
Легаси — это ценный код, который вы боитесь менять.
Например, мы ищем первопричину ошибки или выясняете, куда вставить свою функцию. Мы хотим поменять код, но это трудно, потому что непонятно как не нарушить существующее поведение. Готово — у нас легаси!
Мы переоцениваем сложность незнакомого кода. Поэтому мы думаем, что код, который писали не мы — устаревший. Это работает и с нашими прошлыми проектами, когда мы не можем понять, что закладывали и имели в виду, когда писали эту мешанину на экране.
Хорошие тесты помогают легко менять незнакомый код. А плохие тесты не помогают. Отсюда и определение Физерса.
С легаси помогает время. Парадоксально: обычно время превращает любой код в легаси, но чтобы его понять нам также помогает время. Если вы начали работать над легаси и это трудно — подождите. Да, большая часть кода ужасна, но вы привыкнете и лучше поймете его причуды и особенности.
Легаси не виновато в том, что оно такое. Большая часть кода ужасна, потому что это результат работы многих людей в течение долгого времени с противоречивыми требованиями и под давлением дедлайнов. Это Рецепт Устаревшего Кода™. Когда мало времени и недостаточно знаний — рождаются костыли (ну вы знаете). В конце концов, мы достигнем состояния, когда каждое движение приводит к ошибке, а реализация любой функции занимает целую вечность.
А теперь один из важнейших нюансов.
Легаси — это код, который мы изо всех сил пытаемся понять, чтобы поменять.
Легаси — это личная точка зрения. Устаревший код может стать проблемой для каждого разработчика команды. Какой-то код может показаться сложным, потому что мы его ещё не поняли, а какой-то понимаем, но всё равно чувствуем себя некомфортно, когда рефакторим. Но субъективное ощущение «легаси» зависит от нашего понимания кода, и наших чувств по поводу его изменения. Часто люди этого не понимают.
В итоге мы получаем, что легаси это:
который мы пытаемся понять, чтобы отрефакторить;
Как же эффективно работать с легаси?
Легаси — код, который мы пытаемся понять, чтобы отрефакторить. Задача рефакторинга в том, чтобы сохранить существующее поведение кода. Как без тестов мы будем уверены, что ничего не сломали? Нам нужна обратная связь. Автоматизированная обратная связь — ещё лучше.
Добавить тесты, а затем внести изменения
Логично, что если добавить тесты, они помогут его «прощупать» и он перестанет быть устаревшим. Поэтому первое, что нужно сделать — написать тесты. Только тогда мы будем в безопасности, чтобы рефакторить код.
Но чтобы запустить тесты, мы должны поменять код. Возникает парадокс легаси. Мы обречены? Нет. Поменяем как можно меньше кода для тестов:
Определим точки изменения — «швы».
Первые два пункта самые сложные, а как только доберёмся до тестов, мы знаем, что делать.
Найти «швы» для разрыва зависимостей
Обычно когда мы добавляем тесты к легаси возникает «проблема зависимостей»: код, который мы хотим протестировать, не может работать, потому что ему нужно что-то сложное для тестирования. Иногда это соединение с базой данных, иногда вызов на сторонний сервер, а иногда — параметр, который сложно создать. А чаще всё и сразу.
Чтобы протестировать код, нужно разбить эти зависимости в тестах. Для этого необходимо выявить «швы».
«Шов» — место, где можно изменить поведение программы, не меняя код.
«Швы» бывают разные. Если это объектно-ориентированный ЯП, то обычно это объект, например, в JavaScript.
Допустим, метод connect() вызывает проблемы, когда мы пытаемся поместить код в тесты. Получается, что весь класс — это «шов», который можно поменять. Можно расширить этот класс в тестах, чтобы предотвратить его подключение к реальной БД.
Есть и другие виды швов. Если язык позволяет изменять поведение кода без изменения исходного кода, у нас есть точка входа в написание тестов. Кстати о тестах…
Напишем unit-тесты
Дискуссии о лучших практиках тестирования обычно перерастают в холивары. Применять принцип пирамиды тестов, и писать максимум unit-тестов? Или использовать «Кубок тестирования» и писать в основном интеграционные?
Почему советы такие противоречивые? Потому что у них нет единого определения того, что такое «unit». Одни люди говорят об «интеграционных тестах» и тестируют всю библиотеку, а другие тестируют каждый класс по отдельности.
Чтобы избежать путаницы, Майкл даёт четкое определение того, что такое НЕ unit-тест:
он не работает быстро (
Похожие и интересные статьи:
О том, над чем в целом мы тут работаем: монолит, монолит, опять монолит.
Кратко об истории Open Source — просто развлечься (да и статья хорошая).
Больше новостей про разработку в Додо Пицце я пишу в канале Dodo Pizza Mobile. Также подписывайтесь на чат Dodo Engineering, если хотите обсудить эту и другие наши статьи и подходы, а также на канал Dodo Engineering, где мы постим всё, что с нами интересного происходит.
А если хочешь присоединиться к нам в Dodo Engineering, то будем рады — сейчас у нас открыты вакансии iOS-разработчиков (а ещё для Android, frontend, SRE и других).
9 причин, по которым ваш софт становится legacy
Программисты не любят работать с legacy. Всё потому, что legacy-код сложен, он использует устаревшие технологии и требует особого обслуживания.
Legacy-кодом не рождаются, им становятся. По началу этот код мог использовать новейшие технологии и быструю разработку.
Но внезапно система устаревает, забывается и больше не поддерживается. В чём причина? Вот 9 причин, по которым ваш софт становится legacy.
1. CV вместо кода, как часть IT-карьеризма
IT-индустрия движется быстро, а компании требуют опыт работы с новейшими фреймворками, библиотеками и платформами. Зная это, многие разработчики включают в стек новые технологии для своего резюме.
Руководители проекта часто не догадываются об этом. Разработчики настаивают на использовании чего-то нового. Руководители проекта принимают и продвигают технологию, несмотря на то, что она задерживает проект и портит качество.
Некоторые разработчики рассматривают своё пребывание в компании как временное. Они обеспокоены последствиями выбора технологий меньше, чем следующим шагом в своей карьере.
Чтобы противодействовать этому феномену, руководство должно ставить чёткие, долгосрочные цели, подходить к выбору технологий внимательно и тщательно оценивать изменения в стеке, учитывая интересы компании.
2. Изучать новое – весело
Да, учиться весело. Но обучение само по себе не заработает вам денег, а это, согласитесь, не очень весело. Так почему бы не совместить приятное с полезным? Изучайте новое во время работы и делайте деньги. Не стесняйтесь учиться на работе – так делают многие разработчики.
Однако помните, что изучение нового ради «новизны» – очередная ошибка, ведущая к legacy.
Нововведения приводят софт к состоянию legacy потому, что в системе смешиваются разные подходы и увеличивается сложность. После введения новых вещей старые теряют актуальность. Количество старого кода постепенно увеличивается, и софт переходит в статус legacy.
Для решения проблем разработки существуют готовые инструменты и техники. Другими словами, разработчикам нужно просто делать софт и смириться с отсутствием возможности изучать новое. Для этого компания не должна требовать от разработчиков изучения новых технологий.
В любом случае эти процессы запускает руководство, принимая взвешенные решения об изучении новых технологий в интересах компании. Так изучение становится частью стратегии и перестаёт быть проблемой.
При нововведениях руководству нужно всерьёз задуматься о рефакторинге старого кода. Если весь код обновлён в соответствии с новой технологией, сложность ПО можно держать под контролем.
3. Хайп
Возможно, кто-то удивится, но мода решает в IT многое. Нет, мы не имеем в виду одежду, а проводим аналогию. Дизайнеры сосредоточены на модной одежде с новым дизайном и сочетанием цветов. Желание отличиться и соответствовать некоторым идеалам двигают моду: наверняка вы не хотите носить одежду, которую носит ваш дед. Вместо этого вы бы предпочли стиль любимого актёра, стартапера или музыканта.
То же самое работает в IT. Разработчики отделяют себя от того, что «не стильно», и стараются соответствовать чему-то крутому. В результате появляются разные подходы к разработке без какой-либо объективной причины.
То, что сегодня кажется крутым и новым, завтра устареет. IT-индустрия известна своим быстрым движением. Чем быстрее разработчики осваивают хайп, тем быстрее они отказываются от него при появлении следующего. Некоторые программные системы – это просто музей прошедших трендов. И это характерная черта legacy.
Важно распознавать временные тенденции. Новую технологию нужно тщательно рассмотреть перед внедрением.
4. Преимущества нового и недостатки старого
После внедрения новых технологий недостатки старых видно отчётливо. Но нелегко увидеть проблемы новых технологий. Минусы часто проявляются только после долгих лет использования.
Сравнивая технологии, важно принять во внимание все аспекты. Это может быть тяжело, но сильные решения в отношении технологий защитят ваш софт от судьбы стать legacy.
5. Ненужные зависимости и слишком тесная связь
Повторное использование – это здорово. Во время жизненного цикла ПО происходит утилизация большого количества софта. В результате список зависимостей растёт благодаря таким пакетным менеджерам, как npm и maven.
Если зависимости выбраны невнимательно, они сами могут стать legacy, что в итоге сделает наследственной связанную с ними часть программы. Необязательные зависимости помогут избежать этой ситуации.
Ещё одна проблема – тесная связь, когда большая часть кода взаимодействует с зависимостями. Такая сильная интеграция говорит о том, что судьба конечного продукта находится в руках зависимостей. Зачастую эта связь неизбежна, попробуйте минимизировать её, чтобы софт не перешёл в категорию трудно поддерживаемых.
6. Излишняя сложность архитектуры
Архитектура ПО – это структура высокого уровня. Решения, принятые на этапе создания архитектуры, имеют сильные последствия. Типичный пример – программирование на разных языках. В современном веб-приложении могут использоваться 2-3 разных ЯП. При этом понятно, что от использования шести разных ЯП будет мало хорошего.
Если архитектура излишне сложна, новые разработчики сломают голову, работая над системой. Как итог – ухудшение качества кода.
Непонимание излишне сложной системы ведёт к игнорированию соответствующих особенностей архитектуры.
Максимально простая архитектура избавит вас от этих проблем.
7. Ненужные части языков программирования
Сообщество разработчиков всерьёз рассматривает идиоматический код. Так называют код, который широко использует особенности определённого языка. Принято считать, что идиоматический код легче писать без ошибок.
Но у такого кода есть недостатки. Первый – код навсегда привязан к определённому языку. Когда язык выходит из моды, код уходит в legacy. Менее идиоматический код легче переводить на другой ЯП просто потому, что в нём содержится больше общих конструкций иных языков.
Другая проблема в сложности. Современные языки на удивление сложны. Если вы ещё не ощутили это в своём проекте, посмотрите на сложные задачи и примеры.
Многие из нас знают гуру с 10+ годами опыта. Они знают много подробностей ЯП. Их компетентность позволяет писать самые сжатые решения некоторых проблем. Когда кто-то менее опытный сталкивается с этими проблемами, ему приходится тратить несколько часов на Гугл и обсуждение с коллегами, чтобы просто понять, что вообще происходит.
Эти затраты часто игнорируются, но и они – часть цены за конечное решение. Простейшее решение может требовать больше времени и кода, но впоследствии оно не повлечёт за собой таких же затрат.
Некоторые компании снижают сложность используемого ЯП с помощью гайдов, статического анализа и код-ревью. Как правило, со временем такие гайды изменяются и нарушаются.
8. Неограниченные разработчики
Обычно архитектуру ПО выбирают тщательно. Новый или менее опытный разработчик может и не знать об архитектурных решениях. Такой разработчик может писать код в ущерб архитектуре, тем самым усложняя её. Тогда в коде «всплывает» новая архитектура, которую трудно заметить другим разработчикам. И это ещё один шаг в сторону усложнения.
Иными словами, не ограничивать разработчиков = увеличивать сложность.
Ограничить можно несколькими способами. Например, категоризировать возможности разработчиков, дать доступ к подходящим категориям. Нужно ограничить внесение изменений архитекторами, например, на введение нового ЯП.
Применяйте ограничения через проверку кода с помощью статического анализа. Это единственный способ держать сложность под контролем.
9. Модульная архитектура
Модульная архитектура предполагает разделение софта на малые части с ограниченной областью действия, размером и с чётким разграничением ответственности. Подход был популярен достаточно долго и признан хорошей практикой.
Но со временем модули начали расширяться до группы монолитов. И причина тому – отсутствие ограничений модулей. Например, если модуль ограничен только до функций без состояний, он не будет расширяться за предел этой функциональности. Другой пример – ограничение модуля до абстрагирования взаимодействия с платёжными системами. При этом код-ревью – это верный способ убедиться в соблюдении ограничений.
Заключение
Разработкой софта должны руководить архитекторы и технологи, которые защитят продукт в долгосрочной перспективе. Архитектор должен принимать решения о выборе технологий и справляться с изменениями в стеке. Он должен следить за модульностью, зависимостями и уровнем сложности. Разработчики должны знать области действия модулей и соблюдать ограничения. Руководство может достигнуть этих целей с помощью код-ревью и статического анализа.
Работа с легаси-системами: стабилизация, мониторинг, управление
На самом деле, по-хорошему статью следовало бы назвать так: «Как работать с легаси-системами и сохранять психическое здоровье». Любой, кто имеет с ними дело, меня поймет. Эта статья — попытка обобщения многолетнего опыта знакомства с легаси-системами в виде набора подходов и практических советов. Примеры буду приводить из собственного опыта — в частности, работы с унаследованной Java-системой. Кстати, материалов о работе с легаси в структурированном виде почти нет — оба источника, посвященных именно ей, приведены в конце материала. И это при том, что на легаси приходится чуть ли не половина всего аутсорсинга.
Что такое legacy или унаследованная система
Легаси — в переводе с английского «наследство», и наследственность эта тяжелая. Почти всем доводилось, придя в проект, получить код десятилетней давности, написанный кем-то другим. Это и есть унаследованный код — то есть код исто(е)рический, который часто бывает ужасен настолько, что оказывается вообще непонятно, как с ним работать. И если нам достается легаси-система, то мы, кроме старого кода, также имеем:
Со всем этим нам нужно разбираться и как-то жить дальше. И тут без хорошего чувства юмора, пожалуй, не обойтись — те, кто воспринимают жизнь слишком серьезно, обычно сбегают сразу же, как только увидят настоящее легаси.
На самом деле, легаси-система — это не так уж страшно, и вот почему. Если система жила все эти десять лет и до сих пор работает, значит, какой-то толк от нее есть. Может быть, она приносит хорошие деньги (в отличие от вашего последнего стартапа на новейших технологиях). Кроме того, код такой системы относительно надежен, если он смог так долго выживать в продакшне. Поэтому вносить в него изменения нужно с осторожностью.
Прежде всего, нужно понять две вещи:
Какие задачи нам придется решать, работая с такой системой? Во-первых, мы, очевидно, будем разрабатывать новую функциональность, раз система жива, а значит развивается. Во-вторых, мы будем исправлять ошибки, и это тоже очевидно. И наконец, хотя многие предпочитают про это забыть, мы будем заниматься оптимизацией и стабилизацией системы, даже если напрямую такой задачи перед нами в начале проекта никто не ставил.
Что со всем этим делать
Для успешной работы с унаследованными системами нам придется много пользоваться приемами reverse engineering.
Прежде всего, нужно внимательно читать код, чтобы точно понимать, как именно он работает. Это обязательно — ведь достаточной документации у нас, скорее всего, не будет. Если мы не поймем хода мыслей автора, мы будем делать изменения, последствия которых окажутся не вполне предсказуемыми. Чтобы обезопасить себя от этого, нужно вникать еще и в смежный код. И при этом двигаться не только вширь, но и вглубь, докапываясь до самого нутра. Откуда вызывается метод с ошибкой? Откуда вызывается вызывающий его код? В легаси-проекте “call hierarchy” и “type hierarchy” используется чаще, чем что бы то ни было другое.
Конечно, придется проводить много времени с отладчиком — во-первых, чтобы находить ошибки, и во-вторых, чтобы понять, как все работает — потому что логика обязательно будет такой, что по-человечески прочитать ее мы не сможем. Собственно говоря, дебажить нужно будет вообще все, в том числе и open source-библиотеки. Даже если проблема где-то в Spring, значит, придется отлаживать и, возможно, пересобирать Spring, если возможности его обновить не окажется. Именно так нам неоднократно приходилось делать, причем не только со Spring.
Что касается документации, не лишним будет прибегнуть к тому, что я бы назвал промышленной археологией. Очень полезно бывает откопать где-нибудь старую документацию и поговорить с теми, кто помнит, как писался доставшийся вам код. Возможно, где-то есть старый Confluence, возможно, хотя бы дамп его базы, где вы что-то, может быть, и найдете. Иногда это бывает проще, чем сидеть с дебаггером. Но нередко там окажутся только документы, не имеющие прямого отношения к коду, например, руководства по настройке серверов, которые все в принципе боятся трогать.
Используя эти приемы, рано или поздно вы начнете более или менее понимать код. Но чтобы ваши усилия не пошли прахом, вы должны обязательно сразу же документировать результаты своих изысканий — для этого я советую рисовать блок-схемы или диаграммы последовательности (sequence diagrams). Конечно, вам будет лень, но делать это точно нужно — через полгода без документации вы сами в этом коде будете копаться как в первый раз. А если через полгода с кодом будете работать уже не вы, ваши последователи будет очень благодарны вам за имеющуюся документацию. Кстати, зачастую для себя и для бизнеса документацию нужно готовить разную: в вашей, рассчитанной на инженеров, представители бизнеса ничего не поймут. Им потребуется что-то понятное, описывающее функционирование системы на верхнем уровне. И наконец, нужно не забывать самим пользоваться этой документацией и читать ее. Однажды, решив проблему после двух дней героической борьбы, мы обнаружили собственный документ, подробно описывающий точно такой же случай.
А руками?
Не переписывать!
Теперь поговорим собственно о разработке. Самое важное здесь — вовремя бить себя по рукам и не пытаться переписать весь код заново. Прикиньте, сколько человеко-лет для этого потребуется – вряд ли заказчик захочет потратить столько денег на переделывание того, что уже и так работает. Это касается не только системы в целом, но и любой ее части. Вам, конечно, могут дать неделю на то, чтобы во всем разобраться, и еще неделю на то, чтобы что-то исправить. Но вряд ли дадут два месяца на написание части системы заново.
Вместо этого реализуйте новый функционал в том же стиле, в каком написан остальной код. Другими словами, если код старый, не стоит поддаваться соблазну использовать новые красивые технологии — такой код потом будет очень тяжело читать. Например, вы можете столкнуться с ситуацией, которая была у нас — часть системы написана на Spring MVC, а часть — на голых сервлетах. И если в части, написанной на сервлетах, нужно дописать еще что-то, то дописываем мы это так же — на сервлетах.
Бизнес-интересы и рефакторинг
Нужно всегда помнить, что любые задачи обусловлены прежде всего ценностью для бизнеса. Если вы не докажете заказчику необходимость тех или иных изменений с точки зрения бизнеса, этих изменений не будет. А для того, чтобы убедить заказчика, вы должны попробовать встать на его место и понять его интересы. В частности, если вам хочется провести рефакторинг только потому, что код плохо читается, вам не дадут этого сделать, и с этим нужно смириться. Если совсем уж невмоготу, реорганизовывать код можно по-тихому и понемногу, размазывая работу по бизнес-тикетам. Либо убедить заказчика в том, что это, например, сократит время, необходимое для поиска ошибок, а значит, в конечном итоге сократит расходы.
Тестирование
Понятно, что тестирование необходимо в любом проекте. Но при работе с легаси-системами тестированию нужно уделять особое внимание еще и потому, что влияние вносимых изменений не всегда предсказуемо. Тестировщиков потребуется не меньше, чем разработчиков, в противном случае у вас должно быть все просто невероятно хорошо с автоматизацией. В нашем проекте тестирование состояло из следующих фаз:
И только после прохождения всех этих трех фаз мы можем делать релиз. Кто-то наверняка считает, что сертификация — лишняя фаза, так как на стадии стабилизации все уже выяснено, но наш опыт говорит о том, что это не так — иногда во время регрессионного теста, который прогоняется по второму кругу на другой машине, что-нибудь да вылезет.
DevOps и релиз
При работе с легаси-системой важно наладить все, что касается DevOps и прочих практик, напрямую не связанных с разработкой. В частности, очень хорошо совместно с девопсами на стороне заказчика прописать определенную процедуру релиза, каждый шаг которой будет строго документирован. Только тогда процесс становится предсказуемым и ясным для каждого из участников.
Релизная процедура может быть, например, следующей. Когда заканчивается разработка и пройдены две или три фазы тестирования, мы пишем письмо DevOps-команде за 36 часов до предполагаемого времени релиза. После этого созваниваемся с девопсами и обсуждаем все изменения окружений (мы сообщаем им обо всех изменениях в БД и конфигурации). На каждое изменение у нас есть документ (тикет в Jira). Затем во время релиза все причастные к этому собираются вместе, и каждый говорит, что он сейчас делает: «Мы залили базу!», «Мы перезапустили такие-то серверы!», «Мы пошли прогонять регрессионные тесты в рабочем окружении!». Если же что-то идет не так, запускается процедура отката релиза, точно описанная в изначальном документе на релиз — без такого документа мы обязательно что-нибудь забудем или напутаем (вспомните, в какое время суток обычно происходят релизы).
Организация кода
Очень важно выстроить правильную branching strategy. Основные модели бренчинга давно описаны на сайте того же Atlassian, их можно адаптировать под ваши нужды. Главное — ни в коем случае не коммитить изменения сразу в транк: должны быть stable trunk и feature branches. Я советую делать релизы из релизных веток, а не из транка. То есть у вас есть транк, от которого отходят ветки на конкретные фичи, соответствующие тикетам в Jira. Когда вы закончили разработку в спринте, вы собираете отдельную релизную ветку из готовых фич и ее сертифицируете. Если же что-то пойдет не так, из такой ветки можно будет легко устранить то, что по какой-то причине из релиза в итоге выпадает. Когда же релиз произошел, релизная ветка вливается в stable trunk.
Контроль качества кода
И наконец, code review — это, казалось бы, достаточно очевидная практика, к которой прибегают почему-то далеко не во всех проектах. Очень хорошо, если каждая часть кода проверяется более чем одним человеком. Даже в очень сильной команде в процессе code review обязательно обнаруживаются какие-то косяки, а если смотрят несколько человек, количество выявленных косяков возрастает. Иногда самое страшное находит третий или четвертый reviewer. Но во избежание как саботажа, так и излишнего фанатизма, необходимо договориться, сколько review достаточно для того, чтобы считать фичу готовой.
Для проверки можно использовать пул-реквесты (конечно, если у вас Git), далее есть Crucible и FishEye — оба прикручиваются к Jira. И наконец существует очень удобный инструмент Review Board, который работает и с SVN, и с Git. Он позволяет послать запрос на проверку кода, который соберет в себе все изменения в данном feauture branch.
Как этим управлять
Подбор команды
Самое первое, что должен помнить Team Lead или PM при наборе людей в проект — далеко не всем разработчикам подходит работа с легаси-системами. Даже если человек пишет замечательный код, не факт, что он сможет целыми днями сидеть с дебаггером или документировать чужой код. Для работы с легаси, кроме технических навыков, требуются еще определенные личностные качества — хорошее чувство юмора, самоирония и, конечно же, терпение. На эти качества нужно обращать внимание при подборе людей в команду, а если кто-то не сошелся с легаси характерами, то не воевать с ним, а заменять. Замена человека в проекте в подобном случае не волчий билет, а облегчение и для него, и для команды.
Глупые вопросы
Тимлид не должен стесняться задавать команде «глупые» вопросы и напоминать обо всех вышеперечисленных приемах работы. «Я накатил свежий код, и теперь ничего не работает!» — «А какую ветку ты взял? А базу обновил? А что в логах? А дебажил?» Несмотря на всю свою простоту такие диалоги — неотъемлемый элемент нашей работы, и в особенности с легаси-проектами. Нужно удерживать все в голове и не уставать снова и снова напоминать о каких-то очевидных и не очень очевидных вещах. Без этого, поверьте, не обойтись!
Процесс, или «Здесь так принято»
В силу американской текучки кадров новые менеджеры со стороны заказчика приходят в проект чаще, чем нам хотелось бы. И многие из них, еще не разобравшись в специфике легаси, пытаются внедрять практики и решения из своего предыдущего опыта. Им нужно терпеливо объяснять, почему здесь принято именно так, а не по книжке. Сначала такие вещи донести бывает трудно, но в конечном итоге либо заказчик согласится с вами, либо вы вместе придете к компромиссному решению.
В работе с легаси-системами действительно важен правильно выстроенный, понятный и прозрачный процесс: Jira (или аналог) обязательно должна отражать реальное положение дел в данный момент. Все требования должны быть ясно сформулированы, а процессы четко прописаны. Вся эта Jira-бюрократия точно окупится, сильно снизив степень энтропии в проекте. Так, когда к вам придет заказчик и потребует срочно сделать новую фичу, вы сможете просто показать заполненное расписание. Тогда он легче сможет понять, что чем-то придется пожертвовать.
Что касается эстимэйта (вы же используете Planning Poker, правда?), то оценивать всегда нужно с запасом, чтобы быть готовым к сюрпризам — как мы уже говорили, влияния в незнакомом нам коде зачастую неясны, и порой может вылезать что-то совершенно неожиданное и в неожиданных местах. Так, у нас в проекте был случай, когда изменения в простом CSS сломали часть бизнес-логики: кто-то поставил в JS проверку на цвет элемента интерфейса.
Бизнес, tech debt и SWAT
При работе с легаси-системами нужно стараться противостоять потоку бизнес-требований, которые заказчик будет вам непрерывно поставлять. Заказчик не всегда осознает риски, связанные со стабильностью системы, поэтому вам придется постоянно о них напоминать. Бороться с этими рисками можно двумя способами: балансированием бизнес и стабилизационных задач в каждом спринте или отдельными стабилизационными проектами. Оптимальным кажется баланс 70 на 30, когда 30 % времени каждого спринта вы занимаетесь стабилизацией. Впрочем, заказчик скорее всего не даст вам сделать все, что вы хотите — поэтому записывайте технический долг по мере обнаружения. Из этого tech debt вы будете брать задачи на вышеупомянутые 30 %. А может, заказчик согласится на стабилизационный проект, особенно если вы покажете ему tech debt в ответ на вопрос, почему все в очередной раз упало.
Примеры оптимизации
А теперь вкратце расскажу о способах оптимизации, которыми мы пользовались в разное время.
Во-первых, нужно уйти от традиции ежедневных перезапусков, если так было принято в проекте. Однако делать это нужно, конечно, с осторожностью — продолжать проверять логи и следить за всем, что может привести к падению системы, и бороться с этим. У нас была система, которые перезапускалась каждую ночь, т. к. не могла прожить и двух суток из-за memory и других leaks — теперь же она совершенно стабильно работает от релиза до релиза две-три недели (за редкими исключениями, о которых мы обычно узнаем в 4 утра).
А вот хороший пример того, как делать не нужно. У нас была система, несколько компонентов которой периодически отваливались. Тогда со стороны заказчика пришел девопс и написал скрипты, которые по логам анализируют активность этих компонентов, и, если в логе три минуты нет записей, эти службы перезапускаются. Это, конечно, сработало, но такие вещи должны однажды плохо кончиться.
Очень важный момент — проход по всем логам и составление отдельного эпика. Бывают, конечно, заказчики, которые долго не дают доступа к продакшн-логу. У нас, например, так продолжалось полгода, после чего случился переломный момент, когда нас самих попросили посмотреть логи продакшна. Просмотр затянулся на всю ночь. В системе, работавшей, как считалось, штатно и стабильно, нормальные логи попадались лишь иногда — в основном же записи были со сдвигом вправо и начинались с “at”. Это были сплошные стектрейсы, и их набиралось на десятки мегабайт в сутки. Конечно, мы завели эпик в Jira и создали тикеты на отдельные exceptions. Затем нам пришлось несколько месяцев выбивать время на стабилизационный проект. В итоге мы исправили множество ошибок, о которых никто не догадывался, и сделали логи информативными. Теперь любой стектрейс в них — действительно признак нештатной ситуации.
Еще советую обращать внимание на третьесторонние зависимости как на front-end (Google Tag Manager, Adobe Tag Manager и т. п.), так и на back-end. Например, если у нас на странице есть JavaScript со сторонних ресурсов, нужно посмотреть, завернуты ли эти скрипты в try..catch блоки. У нас были случаи, когда сайт падал из-за того, что ломался какой-то скрипт на стороне. Также важно предусматривать возможность недоступности любых внешних ресурсов.
Ну и последнее: следите за всем, за чем только можно, и грамотно агрегируйте логи. Ведь у вас может быть 12 продакшн-серверов, и вас могут попросить их логи посмотреть, что точно нужно делать не через tail. Мы использовали ELK — связку Elastic search – Logstash – Kibana. Очень полезен мониторинг: мы навесили Java Melody на все серверы и получили огромное количество новой информации, на основании которой многое исправили, осчастливив заказчика.