Шаги, чтобы быть более уверенными при изменении исходного кода.

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

Перед исправлением ошибки

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

Анализ исходящих зависимостей

Почти все объекты, за исключением простых или низкоуровневых, имеют исходящие зависимости - другие объекты, внедренные через конструктор, свойство, метод или статические классы.

public class OrderBuilder
{
    public OrderBuilder(IPriceCalculator priceCalculator)
    {
       ...
    }
}

Интерфейс IPriceCalculator является исходящей зависимостью для класса OrderBuilder.

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

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

Явные и неявные зависимости

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

public class OrderBuilder
{
    public OrderBuilder()
    { }
    public void BuildOrder()
    {
        // 100 lines of some logic, then: 
        decimal price = PriceCalculator.CalculatePrice();
        
        //next 100 lines of code
    }
}

PriceCalculator - это исходящая неявная зависимость класса OrderBuilder.

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

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

Изучить доступные модульные тесты

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

Если модульные тесты имеют информативные имена, например:

UserRepository_NotExistingUserId_UserNotFoundExceptionThrown
OR
OrderWithThePastDateShouldBeConsideredInvalid

достаточно просто просмотреть их имена, не углубляясь в детали реализации модульного теста.

В идеале вы должны начать с проверки модульных тестов, а затем перейти к самому коду, как в подходе TDD.

Анализ входящих зависимостей

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

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

В каждой среде IDE есть команда, называемая чем-то вроде «Найти все ссылки», которая помогает быстро найти, где в приложении используется какой-либо класс или метод.

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

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

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

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

Исправление ошибки

Чем больше времени было потрачено на предыдущий этап, тем меньше усилий будет вложено в текущий.

Написание модульных тестов

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

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

Попытайтесь выяснить, почему все модульные тесты зеленые, даже если в бизнес-логике есть ошибка. Затем следует написать недостающие модульные тесты, чтобы та же ошибка больше никогда не появлялась.

Характеризационные тесты

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

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

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

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

Почини это

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

Такой поэтапный подход позволит вам сделать небольшой шаг назад, когда что-то пойдет не так, а не начинать с самого начала.

После исправления ошибки

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

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

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

После утверждения изменений вы можете объединить исправление в основную ветку и переместить заявку в QA.

Заключение

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

Больше историй о ремонтопригодности