Как выруливать с legacy code, когда проект нужно было на вчера +24


Привет. Меня зовут Иван Мельничук, я Head of Development Department в украинской IT-компании. В публикации хочу поделиться личными профессиональными подходами относительно решения вопроса legacy code в условиях стремительного развития проекта и рассказать о приемах, к которым прибегает наша команда в случаях “когда фичи нужно сдавать “на вчера”.

Разбираемся с проектом


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

image

Именно так выглядит устаревший код. Если мы решим убрать или заменить хотя бы одну карту из этой постройки, мы рискуем завалить весь дом и сровнять его остатки с землей.

Примерно так же “ведет” себя легаси. Поэтому работа программиста, который взялся за задачу модернизировать и “вдохнуть вторую жизнь” в проект, должна быть в некой степени ювелирной. Большинство программистов пытаются избегать и вообще “спрыгнуть с темы” технического долга. Даже составил хит-парад самых распространенных цитат, которые приходилось слышать от программистов, оказавшихся в условиях legacy:

  1. Мы делали “просто” сайт, а теперь вы хотите получить новую “плюшку”, и нам нужно все это переписать, так как у нас легаси...
  2. Никто не знает, как это работает..
  3. Чтобы добавить модуль, необходимо весь сайт проверить — только так мы поймем, что и где может вылезти...
  4. Я туда не полезу ни в коем случае, там уже все плохо…

Но опыт в программировании показывает, что жизнь после legacy существует. Ведь в программировании нет проблем. Есть только задачи, которые нужно решать. И перед составлением плана действий “по преодолению наследственного кода” нужно понять, насколько плохо обстоят дела на проекте в целом. По ходу практики выделил 6 стадий проблемности проекта:

  1. Нет техдокументации. Самый низкий порог проблемности, так как можно потестить и прийти к предварительным догадкам о том, как это все-таки должно работать.
  2. Нет бизнес-документации. Если отсутствует вся документация (и техническая, и бизнес) невольно приходит ощущение “кажется, мы влипли в историю”. Ведь даже бизнес не помнит, как это должно работать, а значит — у команды, как минимум, нет понимания относительно ожидаемого результата.
  3. Нет никого, кто это разрабатывал. Если в придачу к отсутствию документации нет еще и разработчика, который смог бы объяснить, как проект должен был работать, то уже попахивает жареным.
  4. Мы не знаем, что должен получить пользователь в конечном итоге. Одно дело, когда только вокруг бэкэнда возникают вопросы, но если у нас еще нет совершенно никакого представления о том, что должно выводиться на фронте, то это уже близко к состоянию “пациент скорее мертв, чем жив”.
  5. 200+ usage каждой функции и они называются getA. Пятый уровень сложности: когда ты заходишь в легаси, видишь функцию А и 200 usage, и никто не знает, зачем она…
  6. Отсутствуют программисты, которые хотели бы/могли бы разрабатывать. Без комментариев.

Понять бизнес


Бизнес считает деньги. Бизнес не хочет тратить дополнительный финансовый ресурс просто на переделывание того, что уже и так работает. Бизнес никогда не купит идею: “сделаю по-новому потому, что мне это не нравится”. Поэтому всегда предлагайте больше.

Часто разработчики задвигают бизнесу бесполезные идеи. По этому случаю есть отдельный “фонд золотых цитат”.

  • Нанимаем новую команду для качественной “распилки” кода. То есть нам нужна группа специалистов, которая будет делать проект параллельно с уже существующей командой. Не факт, что результат будет отличаться, но бизнес уже должен содержать два проекта.
  • Останавливаем добавление фич, теперь только рефакторинг! Официально вы заявляете бизнесу: Абсолютное табу на фичи, можно только “под капотом” наводить порядок. Неофициально вы сказали: никаких масштабных совершенствований и улучшений проекта не будет, только локально что-то “починим”.
  • У меня на WP уже все необходимое есть, нужно только базу мигрировать. Это “симптом” новичка. Любимая песня джуна: это такой простой сайт, работы всего-то на час-второй…

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

  1. Готов ли бизнес развиваться? Необходимо понять: есть ли у бизнеса запрос на повышение финансовой планки или же просто нужно, чтобы сервис работал более грамотно.
  2. Стадия прототипа? Часто бизнес заказывает нехитромудрый прототип, с помощью которого хочет “прощупать” рынок на момент актуальности продукта. И только если он начинает приносить деньги, бизнес готов развивать фичу в более совершенный и полноценный проект.
  3. Разработка завершена и теперь только поддержка? Важно понимать весь спектр задач.
  4. Хватает ли огня в наших глазах и не потухнет ли он? Проект должен вдохновлять, а не демотивировать. Очень важно, чтобы ваша команда хотела заниматься реинкарнацией легаси, иначе люди постепенно разбегутся на более интересные для них проекты.
  5. Есть ли архитектор? Это самый важный вопрос — есть ли у вас человек, который может сделать правильную архитектуру и начать писать уже хороший код. Нет смысла даже пытаться начинать, если архитектора нет. Иначе через неделю вы станете тем, кто создал проблемный легаси.

Когда будете продавать идеи бизнесу, делайте упор на месседжи “новое, создать, добавить”… Бизнес отлично реагирует на предложения из серии: “Мы сделаем вам новую фичу и при этом проект Х будет быстрее…” и “Чтобы поднять конверсию, добавим новое…”

П — Планирование


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

1. Определение задач, на которых будем паразитировать. Почему паразитировать? Часто бывает: мы продали фичу, а под ней частично подразумеваем рефакторинг.

2. Определение требований высокого уровня. Необходимо прописать четкий запрос бизнеса по “высокой планке”, дабы составить правильную документацию.

3. Определение основных модулей системы, наложение модулей. Перед стартом рефакторинга, нужно понять основные модули системы: где, как, что и с чем может взаимодействовать и разграничивать код на секции.

4. Определение интеграции. При создании конкретного модуля мы должны заранее продумать возможность вмонтировать его в соседний легаси.

5. Определение команды. Один в поле рефакторинга не воин. Команда — очень важный элемент успешного результата. Задор, командный драйв и отличное взаимодействие между участниками процесса must have.

6. Как будем тестить? Если вы собираетесь сдавать качественный проект, то нужно заранее продумать и варианты тестирования продукта.

Наряду с перечисленным выше также важно определить, каким должен быть желаемый конечный результат, составить план масштабирования и прописать узкие места проекта. Например, в случае масштабирования кода важно понимать, где потенциально могут возникнуть проблемы в условиях highload (это может быть база данных, или тяжелые запросы, или сетка, или еще какое-то промежуточное звено, способное “упасть”).

Не менее важно определить границы проекта, где был старый код и где будет новый уровня “шедевр”. Также следует продумать подходы по микросервисам, или DDD, или, возможно, понадобится еще какая-то “наномагия”.

Ниндзя-вские техники работы с легаси


После “подготовительных работ” подходим к техникам, которые облегчают процесс спасения технического долга. Универсального рецепта и панацеи от всех бед в легаси не существует, но по ходу работы на highload проектах составил список ингредиентов, с помощью которых можно приготовить действительно “вкусный” код.

1. Не изобретать велосипед. Для меня это главный лайфхак по написанию кода. Уже все придумано до вас, не стоит прибегать к танцам с бубнами и прочим экспериментам для решения вопросов с legacy code.

2. Код стандарт. Без единого стандарта каждый разработчик будет писать по-своему. “Авторский стиль” поспособствует хаосу и нарастит еще больше код-хлама.

3. Код ревью. Мало только одного наличия код-стандарта. Нужно еще, чтобы в команде был ответственный за его проверку. Иначе все вернется на круги своя, то есть к уровню старого кода.

4. Static Code analyzers, PHP mess detector etc (вместо тысячи книг). Эти и другие автоматические виды техники понадобятся для ускорения процесса, в частности с тем же код ревью.

5. Пробуем микросервисы. Отдельно также могут быть модули или библиотеки. Почему именно микросервисы? Их преимущество в максимальной изоляции логики и ограничении ее определенным API. Плюсом последней является то, что API представляет собой более монолитную сущность по сравнению с “адаптером в коде, которым можно поправить”. Однако у API есть один недочет в виде дополнительных расходов на сеть.

6. Архитектура БД, источники данных. Именно базу данных считаю первым “узким” местом любого легаси. Но каждый проектирует, как хочет, и даже в SQL можно найти неафишируемые недостатки. Вот несколько советов по работе с новой БД:

  • Винт ничто, жажда все. Если вы хотите изменить старую неправильную структуру данных на новый формат данных, более быстрый и производительный, можно пойти двумя путями. Первый — поставить новую базу, полностью удалив старую, и будь что будет. Второй — в случае жесткого легаси параллельно внедрить новый формат. Образно говоря, возле старого дерева посадить новое. Почему второй путь оправдан и более перспективен? Потому что все новое будет работать качественно, а если при деплое или на стадии integration возникнут проблемы, можно просто откатить код назад, при этом нет надобности откатывать все сложные миграции баз данных.
  • Новая БД создается в правильной структуре. При создании нового ресурса важно контролировать те места, в которых пишем новую структуру. Параллельно нужно создать поддержку старой структуры, так как нецелесообразно избавляться от нее полностью. То есть мы продолжаем писать новый материал, а вместе с тем поддерживаем старый шаблон, в который транслируем все новое, и тем самым позволяем старому тоже работать — как бы по-старому, но поддерживая новую структуру.
  • Контролируем всю запись. Не упускаем детали из поля внимания, дабы гарантированно поддерживать БД.

7. Nо SQL? Если с архитектурной точки зрения вы можете оперировать сущностями — оперируйте. Понятие чего-то определённого и конечного поможет вам не создавать ненужные, дублирующие релляции.

8. Декораторы, адаптеры, медиаторы … Эти паттерны одни из главных для интеграции нового кода в старое легаси.

9. План Б, или план отката при интеграции. Многие делают ошибку, что забывают о нем. Он жизненно необходим в ситуации “когда что-то пойдет не так” при заливке нового материала. То есть как только мы начинаем строить архитектуру, уже на этом этапе мы должны понимать, как будем откатывать ее назад в случае бага.

10. Новый код без (доки) тестов через неделю становится легаси. Насколько красивым ни был бы ваш код, без документации через неделю он будет в статусе “legacy” — по причине своей непонятности.

11. Тестирование. Если юнит-тесты не по карману, то используем смоук, функциональные и интеграционные тесты. Насколько реально продать юнит-тесты бизнесу под соусом “чтобы сделать работу красиво?”. В наших реалиях это скорее редкость, нежели закономерность. Если же с “юнитами” по какой-то причине не складывается, то обращаемся к смоук, функциональным или интеграционным тестам, а также не забываем, что можем делегировать задачу, например, мануальному тестировщику.

Вместо эпилога


Самое главное в этой истории — сделать работу и не оставить после себя в наследство легаси и 6 проблемных стадий (приведены в порядке от простой к более сложной):

  • Нет техдокументации
  • Нет бизнес-документации
  • Нет никого, кто это разрабатывал
  • Мы не знаем, что должен получить пользователь.
  • 200 + usage каждой функции и они называются getA()
  • Нет никого, кто хотел бы/мог это разрабатывать.




К сожалению, не доступен сервер mySQL