Главная / Статьи / Археология программного обеспечения: практический метод анализа устаревшего кода

Археология программного обеспечения: практический метод анализа устаревшего кода

Узнайте пошаговый подход к безопасному исследованию недокументированных кодовых баз наследия: от анализа истории коммитов до рефакторинга без нарушения работоспособности системы.

1952 слов

У каждого разработчика рано или поздно возникает особый вид ужаса.

Вы открываете файл — в нем более 4 000 строк. Ни одного комментария, половина переменных названа вроде x2 или tempFinal_REAL, а где-то посередине находится функция doStuff(), которая, судя по всему, отвечает за расчёт счетов во всей компании.

Вы запускаете команду git blame, и след ведёт к человеку, ушедшему из компании шесть лет назад. Поиск в Slack не даёт никаких ответов на вопрос о причинах существования всего этого. Единственный человек, который, возможно, ещё помнит, находится в отпуске, и, честно говоря, он тоже вряд ли будет точно знать детали.

Это археология программного обеспечения.

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

Далее приведен руководство по эффективному выполнению этой работы — без потери здравого рассудка и без сбоев в работающих системах.

Что же на самом деле такое археология программного обеспечения?

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

Это другой вид работы по сравнению с обычным отладкой. Отладка исходит из предпосылки, что вы понимаете систему и в ней что-то сломалось. Археология программного обеспечения основывается на противоположной предпосылке — вы ещё совсем не понимаете систему, и первой задачей является формирование этого понимания прежде чем решиться что-либо изменить.

Это разница между механиком, обслуживающим автомобиль модели, с которой он работал годами, и тем, кто восстанавливает Peugeot 1962 года, который он раньше никогда не разбирал. Один и тот же набор инструментов, совершенно разное состояние духа.

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

Почему эта навык важнее, чем признают люди

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

Банки по-прежнему используют COBOL, написанный в 1970-х годах. Авиакомпании направляют рейсы с помощью систем, которые появились ещё до пилотов, летающих на этих самолётах. Даже стартапы, развивающиеся с высокой скоростью, уже через год-два накапливают устаревший код — код, собранный в спешке под давлением сроков человеком, который впоследствии перешёл в другую команду, для решения проблемы, которая нигде не была зафиксирована письменно.

Если писать новый код — единственное, что вы умеете, то вы ограничены только новыми проектами. Но если вы действительно можете читать старый код — так же, как детектив изучает место преступления, — вы становитесь тем специалистом, к которому обращаются инженерные команды, когда нужно починить что-то сложное. Это профессиональное преимущество, о котором редко говорят открыто.

Мышление археолога

Прежде чем перейти к конкретным техникам, необходимо изменить подход, что значительно упростит всё последующее.

Предполагайте, что когда-то этот код имел смысл для кого-то.

Этот подход имеет большее значение, чем любая другая техника из этого списка. Глядя на запутанный и непонятный код, легко прийти к выводу, что его автор не знал, что делает. Но это почти никогда не является истиной. Гораздо чаще имелась приближающаяся дата сдачи проекта, ограничение, которое теперь не видно, требование системы, исчезнувшее со временем, или требование, которое в 2016 году казалось совершенно разумным, но так и не рассматривалось снова.

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

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

Методы анализа устаревшего кода

1. Читайте историю коммитов как дневник

История Git — это почти то же самое, что настоящая машина времени. Не ограничивайтесь простым осмотром текущего состояния файла — выясните, как он до него добрался.

Выполнение команды git log --follow для конкретного файла часто позволяет увидеть полную картину: функцию, добавленную в спешке непосредственно перед важной демонстрацией для клиента, поспешную правку, загруженную поздно в пятницу вечером, или комментарий вроде «временное решение, удалить после запуска в третьем квартале», который сейчас уже четыре года устарел.

Я наткнулся на странное условие if, предусматривающее особый случай для одного-единственного идентификатора клиента, без каких-либо пояснений. Проверка записей git blame показала примечание автора кода, в котором говорилось, что в производственных данных определённого клиента была ошибка, и что это условие предназначалось лишь для временного удержания всего в рабочем состоянии до тех пор, пока клиент не исправит свою часть. Этот временный решение оставалось в силе пять лет. Понимание обстоятельств помогло команде правильно его устранить — они поступили осторожно, проведя полноценную миграцию данных, вместо того чтобы просто удалить это условие и надеяться, что ничего не сломается.

2. Следуйте за данными, а не только за кодом

Код показывает, что могло произойти. Данные показывают, что действительно произошло.

Обратитесь непосредственно к базе данных для получения информации. Посмотрите на реальные записи. Если в таблице есть столбец status с значениями вроде 1, 2, 7 и 99, не пытайтесь выяснить их значение, полагаясь только на анализ кода — загрузите реальные записи для каждого значения и проследите, что с ними происходит на практике.

Возьмем в пример поле type в старой таблице заказов, где в кодовой базе учитывались только значения от 1 до 5, однако в реальных данных были тысячи записей с значением type = 0. Оказалось, что 0 означает «создано до появления самого поля type» — это часть истории системы, которая исчезла из текущего кода, но по-прежнему присутствовала в данных, полностью видимая.

3. Поговорите с «призраками» (то есть с теми, кто ещё здесь)

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

Лучше задавать узкие, менее напряжённые вопросы вместо общих. Вопрос «Что делает этот код?» побуждает к догадкам, поскольку он слишком расплывчат. Вопрос «Помните ли вы что-нибудь необычное в том, как система оплаты обрабатывала возвраты примерно в 2021 году?» достаточно конкретен, чтобы вызвать реальные воспоминания.

4. Составьте карту перед тем, как что-либо менять

Археологи никогда не начинают раскопки наугад — сначала они составляют карту места. Применяйте ту же дисциплину к кодовой базе.

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

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

5. Пишите тесты перед рефакторингом

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

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

6. Изменяйте по одной вещи за раз

Как только вы наконец поймёте сложную систему, возникает искушение переписать всё заново в одном масштабном, триумфальном pull request. Не делайте этого.

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

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

Всё, что вам удаётся выяснить и понять, стоит сохранить. Запишите это где-нибудь. Даже краткий, неформальный, неполный документ с названием вроде «Как на самом деле работает сервис выставления счётов устаревшей архитектуры» — это подарок тому, кто унаследует этот код после вас; а этим человеком может оказаться вы сами через шесть месяцев, уже забыв всё.

Короткая история: Функция, которую никто не понимал

В одной компании определённая функция получила прозвище «монстр» — 900 строк кода, скрытых глубоко в процессе оформления заказа, к которым никто не хотел притрагиваться. Новых сотрудников предупреждали об этом во время адаптации, наполовину в шутку, наполовину как настоящую предостерегающую историю, подобную местному фольклору.

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

То, что они обнаружили, — это не хаос: оказалась довольно целостная система обработки пяти разных платежных провайдеров, у каждого из которых были собственные особенности и крайние случаи. Её создал человек, решавший реальные, конкретные проблемы в условиях реальных ограничений, без возможности потом вернуться и всё упорядочить. Как только её полностью проанализировали, эта система перестала пугать. Она по-прежнему была сложной, но теперь это была сложность, которую кто-то действительно понимал.

По сути, в этом и заключается вся суть данной дисциплины: превращение страха в карту.

Заключительные мысли

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

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

Поэтому в следующий раз, когда какой-то файл заставит вас захотеть закрыть крышку ноутбука и выйти на улицу, остановитесь и переведите дыхание. То, что вы видите, — это не руины. Это место раскопок, готовое к пониманию.

Возьмите кисть, а не бульдозер.

Связанные статьи