Легаси код что это

от admin

Унаследованный код

Если вы работаете в большой продуктовой группе, то скорее всего за время чтения статей на сайте less.works вы, вероятно, думали: “Этот ресурс содержит столько полезных идей, но у нас пять миллионов строк кода, написанных на нашем собственном языке программирования, которые нам нужно поддерживать. У нас это не сработает”. Тогда эта статья как раз для вас.

Существующий хорошо структурированный переиспользуемый код безусловно является ценным активом. Тем не менее, со временем этот актив может превратиться в унаследованный (legacy code) — с плохой структурой, без автотестов, с неактуальной документацией и большим количеством дублирований. Унаследованный код ограничивает организационную гибкость, и как мы увидим, ведёт к серьёзному ослаблению вашей конкурентной способности. Это статья о том, как писать такой унаследованный код, и том как этого избегать.

Но перед погружением в тему нужно будет отметить, сколько рабочих мест существует благодаря унаследованному коду. Мы путешествуем по миру и часто работаем в развивающихся странах. В этих странах люди вышли из бедности благодаря рабочим местам, созданным для поддержки устаревшего кода. Например, в Индии и Китае несколько городов существенно развились, как в размерах, так и по уровню жизни за последнее десятилетие не без помощи индустрии заказной разработки (outsourcing industry), а большая её часть относится к унаследованному коду. Мы это ценим.

С другой стороны, что бы произошло, если бы всю эту энергию направили на творческие, инновационные продукты? Кроме того, унаследованный код разрушал компании…

_

Рис. 1. Доля рынка браузеров и их версии

Браузер Netscape — один из лучших примеров, который по началу владел рынком. Но в 1995 году компания Microsoft осознала громадный потенциал сети Интернет и начала то, что в последствии назовут “войнами браузеров“ [см. книгу Competing On Internet Time]. В 2000 году она выиграла первую битву этой войны.

Этому произошло по ряду причин. Одна из них в том, что Netscape не выпускала новую версию своего браузера три с половиной года. Но почему? “Потому что браузер был переписан заново без использования прежней кодовой базы Netscape Communicator”. В 2007 году компания AOL, (которая купила Netscape в 1999 году) официально убила браузер Netscape.

Это статья решит все ваши проблемы с унаследованным кодом… ладно, а может и нет. Но она определённо притупит вашу боль от унаследованного кода и возможно когда-то, вы сможете полностью от него избавиться.

Как Писать Новый Унаследованный Код

Писать унаследованный код просто — мы объясним это на примере нескольких простых шагов. За десятки лет компании создали кучу унаследованного кода. В компании Xerox мы однажды услышали: “Нам давали много уроков, но выучили мы лишь часть”. Это особенно касается унаследованного кода. Мы из раза в раз проходим этот урок, но почему-то так и не можем его выучить.

Как давно это началось? Ещё в 1967 году, возможно, в первой книге по управлению проектами разработки Чарльз Лехт (Charles Lecht) писал:

Ответственность за запуск заранее обречённого проекта в равной степени лежит как на менеджменте, который настаивает на фиксации определённых обязательств со стороны программистов, так и на самих программистах, которые не понимают то, на что соглашаются. Слишком часто менеджмент не осознает, что, прося сотрудников о “невозможном”, сотрудники будут чувствовать себя обязанными согласиться из уважения, страха или ложной лояльности. Чтобы сказать начальнику “нет”, часто требуется смелость, политическая и психологическая мудрость, а также понимание бизнеса, которые приходят с большим опытом.

Ниже указаны явные причины появления унаследованного кода:

И конечно в этих причинах лежат ключи к предотвращению его возникновения…

Как Избежать Написания Унаследованного Кода

Избегайте нереалистичных сроков и фиксированного объёма работ

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

Многие компании застряли в порочном круге навязанных обещаний и невыполнимых обязательств. Сегодня, в эру высоких скоростей, клиенты ‘принуждают’ их обещать слишком многое. “Если вы не можете поставить к концу года, мы купим у вашего конкурента, который даст такое обещание”. Отдел продаж или руководство могли бы отреагировать, проявив прозрачность и стремясь к взаимовыгодным долгосрочным отношениям (сотрудничество с заказчиком), но вместо этого они озадачены, прописана ли неустойка в договоре за несоблюдение приемлемых сроков (согласование условий контракта), и отвечают: “Да, без проблем, мы это сделаем!”. После этого тот же цикл начинается уже внутри организации. Руководство приказывает главе разработки “сделать это” или “сделать так, чтобы это случилось”, потому что “это обещано клиентам”. Обещание путешествует по организационной структуре к разработчикам, которые не могут передать его дальше.

Как разработчик обычно реагирует? Чарльз Лехт уже нас предупреждал более 50 лет назад: Разработчик будет “обязан согласиться из уважения, страха или ложной лояльности” и неохотно, но соглашается на эти сроки. Разработчик открывает свой секретный ящик с инструментами и делает всё возможное, чтобы успеть к ближайшему сроку, используя инструменты, такие, как хардкод, копипаст-программирование, игнорирование тестов, сверхурочные и другие, разрушающие качество и срезающие углы практики см. книгу The Enterprise and Scrum. Никто не обращает внимание на использование этих ‘инструментов’, поэтому что сроки соблюдены. Менеджмент награждает разработчиков за их тяжёлый труд и восхваляет их за “отличную командную работу” и “боевой дух”.

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

_

Рис. 2. Динамика нереалистичных сроков

Боб Мартин в книге Чистый Код утверждает, что настоящие мастера своего дела не давали бы таких невыполнимых обещаний, а проблема унаследованного кода могла быть решена обучением разработчиков, чтобы повысить их профессионализм.

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

Однажды в Европе мы посетили директора большой продуктовой группы (которая разрабатывала встраиваемые системы) и его команду менеджеров. Директор объяснил, что группа успешно выпустила последний релиз, и поставил под сомнение необходимость внедрения масштабируемого Скрама. В этот момент один из его подчинённых сказал: “Ну, на самом деле ближе к дате релиза мы сильно отставали, поэтому мы много работали сверхурочно, выдернули более сотни человек из другого продукта нам в помощь. Поэтому мы и успели. Сейчас мы серьёзно отстаём от графика, потому что в последнем релизе было создано так много плохого кода, что мы тратим бóльшую часть времени на исправление дефектов, о которых сообщают нам наши клиенты, и вынуждены работать с беспорядком в кодовой базе”.

Обратите внимание на взаимосвязь между этими событиями и отсутствием бережливого мышления. Например, в этих случаях прослеживается утрата связи с реальностью (waste of wishful thinking). Один из трёх источников потерь в бережливом мышлении — перегрузка (overburden) — часто можно увидеть, как героический рывок ближе к концу релиза приводит к ещё большим потерям в будущем. Также отсутствует культура остановись и исправь (stop and fix) — а наблюдается противоположная ей “нет времени точить, нужно пилить” (carry on and don’t fix).

Если вы скажите своим клиентам: “Мы не понимаем, что происходит, и понятия не имеем, когда это будет сделано”, то это будет коммерческим самоубийством. Мы часто слышим от руководителей фразу: “Либо мы берём на себя нереалистичные обязательства, либо у нас не будет работы”. Это убеждение является ложной дихотомией.

Есть другой способ, когда клиент и заказчик вместе понимают суть продуктовой разработки: она не предсказуема на 95%. Вы можете принять эту реальность, если будете демонстрировать прозрачность по отношению к своим клиентам во время разработки. Например, …

  • предоставляя информацию о текущем статусе разработки вашим ключевым клиентам каждую итерацию; например, с помощью Диаграммы Сгорания Релиза (Release Burndown Chart) и обновлённого Бэклога Продукта
  • давая возможность ключевым клиентам предоставлять обратную связь по приоритетам и изменению целей по мере того, как они видят, как идут дела, а затем корректируя план соответствующим образом
  • предоставляя оценки с указанием их точности (вероятности) или несколько оценок (по разным сценариям) см. книгу Вальсируя с Медведями
  • используя другие техники, которые поощряет частое взаимодействие с клиентами, основанные на реальности и прозрачности

Благодаря изменениям в том, как продуктовые компании взаимодействуют со своими клиентами, снижается давление, приводящее к созданию унаследованного кода.

Часто менеджмент прибегает к “быстрому решению” (quick-fix) в ответ на давление рынка — ‘заказать’ разработке “ещё ресурсов”, так как это ‘дёшево’. Мы работали с продуктовой группой, которую принудили нанять сотни людей в течение года. Исключение? Нет, вот ещё пример: лидер продуктовой группы, с которой мы работали, недавно был ‘назначен‘ руководить новым продуктом. В этом продукте работало 900 человек в 12 разных офисах и 20 активных филиалах. Этот продукт отставал от конкурентов, и предыдущее руководство пыталось спасти его, добавляя больше людей — теперь они отставали ещё больше.

Вот ещё урок, который даётся снова и снова. Возможно, первым крупномасштабным проектом в мире была SAGE система, которая разрабатывалась в 1950-х. Команда проекта торопилась, поэтому…

В течение года примерно 1000 человек были вовлечены в разработку системы SAGE. Люди из самых разных слоёв общества были наняты и обучены. Проводники трамвая, сотрудники похоронного бюро (изучавших математику не менее года), школьные учителя, мойщики окон и другие в спешном порядке были собраны, обучены программированию в течение нескольких недель и распределены по разным отделам очень большой организации… Изначально ожидаемый объем работ был значительно уменьшен. Первый релиз состоялся с опозданием на год и значительно превысил бюджет 1 .

Вместо воспитания великих разработчиков или найма небольшого количества крутых людей они сконцентрировались на найме максимального количества “голов” (англ. heads, head count) что, в свою очередь, привело к поспешной и неполноценной образовательной программе для новых сотрудников. Это быстрое решение приводит в целом к низкому уровню навыков разработки в командах, снижает вероятность стать хорошими разработчиками и, в конечном итоге, к всё значительному увеличению количества плохого унаследованного кода.

Низкие Навыки Разработки

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

В целом по нашим наблюдениям уровень навыков разработчиков в больших продуктовых группах довольно низкий. Разработчики часто не знакомы с хорошими базовыми техниками — простыми практиками, такими как сокрытие и инкапсуляция, или принципами хорошего дизайна. От разработчиков встраиваемых систем мы иногда слышим восклицания: “Это техники для ООП, а мы пишем на языке Си”. Но они не понимают, что некоторые из этих идей были изначально разработаны не для объектно-ориентированных технологий 2 . Мы наблюдали следующий зависимость:

Чем больше продуктовая группа, тем меньший объём знаний ‘современных’ практик разработки

Но эти практики являются основой для бесконечно повторяемого процесса разработки. К счастью, навыки разработки зависят не только от чистого таланта; они могут быть изучены и улучшены:

  • в школах
  • при поддержке организаций
  • путём самообучения

Лидеры продуктовой группы могут думать, что понимают, как работают образовательные программы, но это может быть не так…

Школы — В университетах не преподают базовых навыков разработки. Есть шокирующий разрыв между академическим образованием и коммерческой разработкой. Многие преподаватели никогда не работали разработчиками и не видели долгосрочную динамику развития навыков разработки и унаследованного кода. Им также не хватает практики Go See. Некоторые университеты недавно добавили практики Agile-разработки в их учебную программу по информатике. Это хорошо. Однако требуется глубокий опыт, чтобы действительно понять Agile-практики такие, как разработка через тестирование (TDD), а преподаватели редко имеют такой опыт.

Поэтому, не ожидайте от выпускников университетов, что они будут иметь хорошие навыки, особенно в Agile-разработке.

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

В отличие от них бережливые (lean) организации инвестируют в обучение своих сотрудников. Одно из исследований показывает, что японские компании тратят в восемь раз больше на обучение новых сотрудников, чем в США, и в два раза больше, чем их европейские конкуренты [см. книгу The Machine That Changed the World].

Организации также не осознают необходимость постоянного улучшения. Им необходимо не только обучать базовым навыкам, но и создавать среду, в которой сотрудники постоянно сталкиваются с вызовами и учатся отвечать им. Как? Менеджеры в роли учителей, коллеги, обучающие друг друга (например, с помощью парного программирования), а также внутренние или внешние выделенные коучи — все это поддерживает культуру обучения и постоянного совершенствования.

Самообучение — Многие разработчики не следят за развитием своих навыков. Гуру по качеству ПО Филип Кросби (Philip Crosby) назвал недостаток знаний, вызванный нехваткой обучения, главной причиной плохого качества.

Люди подсознательно тормозят собственное развитие. Они начинают полагаться на клише и привычки. Когда они достигают определённого уровня личного комфорта, они перестают учиться, и их ум остаётся бездействующим до конца своих дней. Они могут подниматься по карьерной лестнице, могут быть амбициозными, напористыми и даже работать с утра до ночи. Но они больше ничему не научатся. Филип Кросби (Philip Crosby) Quality Is Free, 1980.

В 1999 году Дейв Томас (Dave Thomas) и Энди Хант (Andy Hunt) опубликовали превосходную книгу Программист-прагматик. Путь от подмастерья к мастеру], резюмирующую мировоззрение и поведение современного профессионального разработчика. Мы призываем людей прочитать её и взять на себя ответственность за собственное развитие.

Избегайте тривилизации разработки

Я архитектор, написание кода — этой низкоквалифицированной работой должны заниматься другие люди”. Мы слышим подобные заявления от архитекторов из “башни из слоновой кости”, которые считают, что программирование ниже их уровня. Организация, в которой работают такие архитекторы, создала культуру тривилизации (упрощение, признание обыденным, англ. trivializing) программирования. Такая культура принижает значение кода, обесценивает написание чистого кода и познание навыков программирования. В такой культуре люди хотят подняться по социальной и организационной лестнице — а это означает отход от программирования. Программирование является всего лишь ранним этапом карьеры, который им предстоит пройти. Такая культура поощряет распространение унаследованного кода.

Организации тривилизируют разработку при помощи:

  • аутсорсинга разработки
  • карьерного роста
  • разницы в зарплатах

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

Карьерный рост — Крупные организации хотят обеспечить будущее для своих сотрудников; типичное решение — это установленные карьерные пути для менеджеров и инженеров. Люди, идущие по пути менеджмента, уходят от технической работы и становятся “профессиональными менеджерами”. Те, кто идёт по техническому пути, тратят своё время на написание “архитектурных документов”. Какой бы карьерный путь вы ни выбрали, в нем не будет программирования.

Разница в зарплатах — Из всех профессий, связанных с разработкой программного обеспечения, зарплата программистов в среднем одна из самых низких (см. книгу Applied Software Measurement). Естественно, и к сожалению, эта разница в заработной плате не способствует тому, чтобы стать лучшим разработчиком, а вместо этого способствует прекращению работы в в этом качестве. Есть ли альтернатива? Пит МакБрин (Pete McBreen) продвигает модель мастерства в области программного обеспечения, в которой зарплата напрямую связана с навыками. Навыки разработчиков оцениваются по портфолио и отзывами коллег.

Повышайте осведомлённость о негативном влиянии унаследованного кода

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

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

Мы призываем технических руководителей активно просвещать свои бизнес- и техническое сообщества по этому вопросу и осознавать стоимость владения унаследованным кодом.

Хорошо, у нас есть унаследованный код, что дальше

Вы, вероятно, узнали причины появления унаследованного кода, и его у вас уже кучи. Как от него избавиться? В своей книге Working Effectively with Legacy Code Майкл Физерс (Michael Feathers) рассказывает о конкретных техниках работы с кодом для его постепенного улучшения. В этой статье мы не будем повторяться, рекомендуем прочесть книгу Физерса. Но мы рассмотрим некоторые общие стратегии работы с унаследованным кодом.

Не переписывайте унаследованный код

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

В продуктовой группе с 30-летней кодовой базой разработчик спросил нас, можем ли мы помочь в рефакторинге функции из 5000 строк. Мы думали, что он преувеличивает. Но когда мы сели в пару и посчитали количество строк функции, мы обнаружили, что она даже немного больше 5000. Как могла быть создана такая функция? Просыпается разработчик и думает: “Боже, какой сегодня чудесный день! Напишем-ка функцию из 5000 строк?”. Вероятно, что нет. Когда разработчик пишет новый код, он обычно пишет его с приличным качеством. Но со временем качество ухудшается. И функция вырастает до 5000 строк. Почему так происходит? Клиент запрашивает новую фичу, и она впихивается из-за плохих навыков разработки или нереалистичных сроков. Качество кода снижается, а усилия, необходимые для внесения изменений, возрастают (см. рис. 3).

_

Рис. 3. Качество кода снижается со временем

Через какое-то время вносить изменения в код становится слишком болезненно и для этого требуется слишком много усилий; разработчики начинают просить его переписать. Сначала Владелец Продукта отказывается — переписывание означает высокие затраты без добавления новой ценности. Но по мере того, как скорость разработки падает, разработчики жалуются все больше, и в конечном итоге Владелец Продукта ‘соглашается’ переписать. В это время способность реагировать на изменения — новые требования — равна нулю. И после окончания переработки код получается качественным, а значит, и новая разработка идёт быстрее (см. рис. 4).

_

Рис. 4. Переписывание улучшает качество

Что происходит после? Спешка при реализации новых требований приводит к “срезанию углов” только что вычищенного кода, что снова приводит к ухудшению качества и увеличению усилий по разработке (см. рис. 5). Через некоторое время разработчики требуют ещё раз всё переписать. В некоторых крупных продуктовых группах мы видели, как компоненты переписывались трижды.

_

Рис. 5. Качество кода снижается вновь после переписывания

Key insight: The problem is not having legacy code, it is creating legacy code.

Ключевая идея: Проблема не в наличии унаследованного коде, а в его создании.

Мысль в том, что нужно предотвращать создания нового унаследованного кода, а не бороться с ним самим. Фокус должен быть на выращивании здорового кода и не давать ему ухудшаться со временем. Но как? Улучшайте качество кода каждый раз, когда его меняете. “Если бы мы все оставляли после себя наш код немного чище, чем когда мы за него взялись, то код попросту не будет загнивать” — утверждает Боб Мартин (см. рис. 6).

Читать:
Cisco any connection почему перестает работать интернет

_

Рис. 6: Выращивание здорового кода

Улучшайте код по соседству

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

_

Рис. 7: Улучшайте код по соседству

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

Пишите высокоуровневые и модульные тесты

Нас часто спрашивают, начинать ли с модульных тестов или с высокоуровневых тестов. Ещё одна ложная дихотомия. Они все важны! Часто проще добавить высокоуровневые тесты к унаследованной кодовой базе, они дают уверенность, что существующая функциональность работает. Но модульные тесты выполняются быстро. Быстро выполняемые модульные тесты помогают при пошаговом рефакторинге унаследованного кода. Поэтому пишите модульные и высокоуровневые тесты при улучшении кода по соседству.

Переписывайте мёртвый унаследованный код

Иногда невозможно постепенно провести оздоровление кодовой базы. Предположим, что часть низкоуровневого кода написана на PL/M (устаревший процедурный язык, прим. переводчика), и его никто не хочет изучать. Или часть вашего кода написана на доморощенном языке, компилятор которого работает только на VAX/VMS (проприетарная серверная операционная система, прим. переводчика). Когда постепенное изменение невозможно 4 — то унаследованный код уже мёртв, т.е. необходимо ‘ампутировать‘ эту часть кода вместо того, чтобы позволить ей убить ваш продукт. 5

При замене мёртвого кода:

  • покройте его тестами
  • не добавляйте функциональности в старый код
  • не добавляйте функциональности в заменённый код

Заключение

В мире существуют миллиарды строк унаследованного кода, и их количество увеличивается с каждым днём. Это уже создавало огромные проблемы в прошлом (например, Проблема 2000 года), и будет в будущем. Но унаследованный код не исчезнет, если не будут устранены первопричины:

  • нереалистичные сроки и фиксированный объём работ
  • низкие навыки разработки

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

Что делать с существующим унаследованным кодом? Лучше постепенно улучшать код, чем полностью заменять его. Это требует инвестиций в навыки разработки и применения современных практик, таких как TDD. Единственный способ вырастить лучший код — это вырастить блестящих сотрудников. Это тематика бережливого мышления.

Рекомендуем к Прочтению

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

    Майкла Физерса. Конкретные советы о том, как постепенно улучшать унаследованную систему на уровне кода. Мартина Фаулера. Классическая работа про улучшение существующего кода. Билла Вейка. Конкретное руководство по совершенствованию рефакторинга кода. Джошуа Кериевски. В этой книге Джошуа объясняет, как постепенно реорганизовать код по стандартным надёжным шаблонам проектирования. Стефана Рука и Мартина Липперта. Для больших систем может потребоваться большой рефакторинг. В этой книге объясняется, как это сделать мелкими шагами, чтобы ваши системы оставались стабильными.

Следующий материал охватывает организационную динамику, связанную с унаследованным кодом:

    Кена Швабера. Глава 9 — одно из немногих описаний, объясняющих взаимосвязь между обещаниями клиентов и созданием унаследованного кода. Кэвина Тейта. Эта книга не охватывает многих новых техник, но даёт отличный обзор методов устойчивого создания программного обеспечения.

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

Legacy code

Legacy code is the bane of most developers. But it is a reality we all have to deal with.

It’s code you’ve inherited from someone else, maybe from way older versions of your app. And often it comes without any proper documentation, a lot of smells, and a nagging feeling that “we should probably rework this… but it works as is though”.

I’m sure that some developers will spend most of their time on relatively new codebases, ones they will build from the ground up — and that they’ll never deal with legacy code as such.

Even then, I’m of the opinion you can inherit legacy code from yourself — or more accurately from “5 years ago"-yourself.

I remember getting to use code that was seemingly constructing a very long string, based on a lot of options, parameters and collections. And then, it eval'd it.

When I went to see the colleague who built the library, turns out that at the time they just didn’t know how much metaprogramming you could do in Ruby without ever needing an eval call.

Some other parts of the code were straight-up reimplementation of things the framework could do by itself. “We started 8 years ago with version 1 dot something" — we were now at version 4.2.

Time makes foreign all things, I guess.

I should clarify, legacy code is not bad code per se. It is only old, or a bit more complex than it should and the author is nowhere to be found.

And I think that’s the approach we tend to have most often towards legacy code. We tend go nuclear, rewrite it all and damn those who came before us.

Turns out that’s not so great.

Lack of pre-written tests, lacking comprehensive knowledge of the code being rewritten and poor structure in our refactoring operations often leave us with a mess of code that only seem better to us than the previous one because this time, it’s made by us.

I’ve left behind me quite messy code. Which then turns into its own legacy code.

What to do then?

It turns out, dealing with legacy code is not spring cleaning. It’s more like improving your environment. You upgrade parts of it, one by one, whenever you stumble upon something whose improvement is in your capabilities.

It’s writing a test for that modification you had to make on that one old library, it’s annotating bits of hackey code after spending a while figuring them out, it’s replacing that one while loop by a for or an each.

Legacy code is never a business priority. Trying to make it one will often turn it into new, soon-to-be legacy code. Let’s just factor it in our task estimates and do whatever little we can whenever we can about it instead.

Что такое legacy code?

Не могу найти перевод. Можете объяснить в двух словах что это такое? Что за понятие?

Kyubey's user avatar

Legacy code — тяжелая наследственность : ) Устаревший код, который более не поддерживается и не обновляется, но используется. Второе значение — код от сторонних разработчиков, или из старых версий.

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

Legacy code — код, подпадающий под один или несколько признаков:

  • написан другими разработчиками, возможно, уже недоступными для контакта;
  • не покрыт юнит-тестами;
  • сохранён для совместимости с предыдущими версиями системы;
  • устарел и/или более не поддерживается в связи с развитием системы (написан на предыдущем языке, под старую архитектуру, аппаратную платформу, операционную систему и т.д.).

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

введите сюда описание изображения

@sergiks все написал верно, но все таки уточню: вообще термин Legacy в программировании означает прилагательное означающее принадлежность к традиционному. Скажем, Legacy Driver — означает драйвер от производителя и т.д.

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

Код без тестов — легаси

Если вы работаете в 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-тест:

он не работает быстро (< 100ms / test);

он взаимодействует с инфраструктурой, например, базой данных, сетью, файловой системой, переменными;

Напишите максимум тестов, которые обладают этими 2 качествами, при этом неважно, как вы их назовёте.

Иногда трудно написать такие тесты, потому что не понятно, что код должен делать. Тогда используйте специальную технику.

Тесты для определения характеристик

Это тесты, которые формализуют фактическое поведение части кода.

Вместо того чтобы писать комплексные модульные тесты, мы фиксируем текущее поведение кода — делаем снимок того, что он делает. Тест гарантирует, что это поведение не изменится!

Это мощная техника, потому что:

В большинстве систем то, что код делает важнее того, что он должен делать.

Мы можем быстро покрыть легаси с помощью этих тестов. Так мы подстрахуемся для рефакторинга.

Этот метод также называют «Approval Testing» («тестированием одобрения»), «Snapshot Testing» или «Golden Master».

Но обычно на всё это очень мало времени.

Когда совсем нет времени на рефакторинг

Несколько советов, если предыдущие не подходят.

Большие куски кода обладают «гравитацией» и привлекают ещё больше кода. «Теория разбитых окон» в действии: небольшой беспорядок влечёт за собой беспорядок серьёзнее. Если класс уже содержит 2000 строк, то какая разница, что вы добавите еще 3 if оператора и будете поддерживать класс длиной в 2010 строк?

Это всего лишь 3 if: тяжело себя убедить, что нужно потратить на них 2 дня, хотя и должны. Что делать, если действительно нет времени писать тесты для этого класса? Используйте техники Sprout (прорастание), Wrap (обёртывание) и скретч-рефакторинг.

Sprout

Напишите код в другом месте, сделайте один тест, определите, где вы должны вызвать этот код из существующего кода (точка вставки), и вызовите свой код из легаси.

Рассмотрим на примере:

Допустим, нам нужно убрать дубли файла entries, но postEntries() трудно проверить — нет на это времени. Мы можем «прорастить» код где-то ещё, например, в новом методе uniqueEntries(). Этот новый метод легко протестировать, потому что он изолирован. Затем вставим вызов этого метода в существующий, не проверенный код.

Минимальные изменения, минимальный риск. Можете «вырастить» один метод, целый класс или что-то ещё, что изолирует новый код.

Можно «обернуть» изменение, если оно должно произойти до или после существующего кода.

Переименуем старый метод, который хотим обернуть.

Создадим новый с тем же именем и подписью, что и старый.

Вызовем старый метод из нового.

Поместим новую логику до/после вызова другого метода.

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

Ещё один способ решить эту проблему — это обернуть её, поэтому мы переходим к postEntries(), списку записей, из которых мы удалили дубли.

В тестах мы бы изменили проблему postEntriesThatAreUnique(), чтобы проверить, работает ли логика удаления дубликатов. Разница может быть ещё больше.

Эти методы не идеальны, и у них есть недостатки. Но это полезные инструменты при работе с легаси. А при необходимости можно даже немного нарушить правила.

Скретч-рефакторинг

Сложно работать с кодом, который мы не писали, без тестов и с плохой документацией. Чтобы «выбраться» нам нужно разбить зависимости, написать тесты. Но с чего вообще начинать, когда код непонятен? Хорошая техника — скретч-рефакторинг.

Его цель в том, чтобы ознакомиться с кодом, а не менять. Мы «играем» с кодом столько, сколько захотим: извлекаем функции, упрощаем, переименовываем переменные. Как только сделаем, всё, что нам нужно — откатим всё обратно и начнём с правильных тестов.

Выводы

Легаси будет везде, где бы вы ни работали, в каждой кодовой базе. Можно сопротивляться и чувствовать себя плохо, когда вы застряли в нём. А можно рассматривать это как возможность. Работа со старым кодом это очень ценный навык, его надо изучать теоретически (почитайте книгу «Working Effectively with Legacy Code») и практиковать в ежедневных задачах.

Похожие и интересные статьи:

О том, над чем в целом мы тут работаем: монолит, монолит, опять монолит.

Кратко об истории Open Source — просто развлечься (да и статья хорошая).

Больше новостей про разработку в Додо Пицце я пишу в канале Dodo Pizza Mobile. Также подписывайтесь на чат Dodo Engineering, если хотите обсудить эту и другие наши статьи и подходы, а также на канал Dodo Engineering, где мы постим всё, что с нами интересного происходит.

А если хочешь присоединиться к нам в Dodo Engineering, то будем рады — сейчас у нас открыты вакансии iOS-разработчиков (а ещё для Android, frontend, SRE и других).

Related Posts