Pull to refresh

Comments 9

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


Вот классический (но не единственный) пример: https://github.com/emilybache/GildedRose-Refactoring-Kata

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

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

Это тяжелый случай. Я работал с таким – у нас был самописный движок, сотни тысяч строк кода, и полный ад с развитием и поддержкой продукта.


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


Дальше покрытие тестами, рефакторинг.


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


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


Если у кого-нибудь есть варианты лучше, отпишитесь, проблема насущная.

я видел такой код, просто страшно

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

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

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

Для начала нужно определиться с бизнесс-сценариями, формализовать их, написать актуальные доки. Чтобы было понятно, какие ожидания от тех или иных действий и уровни связности.

Если это какое-то сетевое API, то написать интергационные/smoke тесты как можно подробнее и потом начинать рефакторить по чуть-чуть, одновременно добавляя новые и обновляя старые тесты. Также не забыть про введение версионирования API и тестов, если им пользуются разные сущности (сайт, внешние сервисы, мобильные приложения, etc). Проверять тестами четкий список поддерживаемых версий. Это позволит в будущем относительно безболезненно выкидывать нерабочий код, как из кода API, так и соответствующего кода тестов для таких устаревших версий.

Если это код какого-то монолита, то составить список классов (функционал + данные) и дробить на максимально возможные сущности с минимальной связностью. Добавлять интерфейсы/протоколы там, где цель в управлении иерархией типов классов. Потом для этих отделенных сущностей написать модульные тесты, возможно даже вынести их в виде библиотек или даже сетевых сервисов (работающих по тому же сокету), просто ради создания изоляции (такой вариант с сетью удобен для проверок, но не всегда оптимален). Выделить совоеобразный «черный ящик» и уже к нему описать проверочные условия и ожидания. И потом всё аккуратно рефакторить с небольшими коммитами, которые сразу связаны с нужными кусками кода тестов.

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

Не бывает невозможного рефакторинга. Бывает только недостаточность на него ресурсов и/или квалификации.
Часть этих проблем решает отдельный штат специалистов по тестированию (специальные люди + тест кейсы + время + вдумчивое человеческое тестирование функционала). Не все проекты это могут позволить, но всё же это помогает избавиться от страхов изменять код.
К сожалению, на легаси код писать модульные тесты очень сложно или невозможно. Другие виды тестов проверят только самые базовые и очевидные сценарии или их будет слишком много и велика вероятность регрессии во многих «неучтенных» сценариях.
Тема очень пересекается с тем, что говорил Dylan Beattie на DotNext 2 года назад в докладе «Ctrl-Alt-Del: learning to love legacy code»: www.youtube.com/watch?v=afZGZOL6Fr8
Sign up to leave a comment.

Articles