Значи ще започнете да поправяте част от техническия си дълг? Страхотен! Просто запомнете едно основно правило: Не нарушавайте приложението.

Помислете за това – когато трябва да поемем технически дълг, бизнесът плаща алтернативни разходи, за да преработим кода, вместо да създаваме нови функции или дори да коригираме съществуващи грешки. Това е добре и необходимо и има „начини да кажете на бизнеса колко важна е тази дейност“, но единственото нещо, което не можете да направите, е следното:

Когато плащате технически дълг, не можете да причинявате нови грешки.

Това е еквивалентът на медицинската Хипократова клетва (Първо, не вреди). Разбира се, извършвате операция на код вместо на жив пациент, но разликата е същата. Ако всеки път, когато бизнесът ви позволи да направите кода по-добър, вие причинявате дефекти, изправени пред потребителите, бизнесът няма да ви позволи да правите твърде много опити за почистване на кода в бъдеще.

И така, как да направим жизнено необходимата „операция на кода“, като същевременно минимизираме риска за крайните потребители?

Както се оказа, имам някои идеи.

Преди известно време изнесох беседа относно техниките за тестване на единици на .NET и открих, докато се подготвях, че моите идеи за подобряване на кодова база са склонни да се сортират в 5 кофи или фази, които се появяват приблизително последователно:

  1. Изградете предпазна мрежа
  2. Подобрете възможността за тестване
  3. Подобрете вашите тестове
  4. Преработете кода
  5. Разширете вашите тестове

Чрез многократно преминаване през този процес по време на почистване на кода, можете прогресивно да подобрявате кода си, като същевременно минимизирате риска, печелейки си по-голямо доверие и свобода на действие в бъдеще, както и по-чист и по-поддържащ се код.

Изградете предпазна мрежа

Въпрос номер едно, който задавам, когато се подготвям за промяна на кода, е следният: имам ли тестове или процеси, които ще уловят грешка, ако нещо ми мине?

Тази защитна мрежа може да бъде модулни тестове, планове за регресионно тестване или дори такива тривиални неща като компилатор или пакет за анализ на код.

Ако нямате предпазна мрежа, която покрива основните случаи на използване на вашето приложение, трябва да създадете такава, преди да промените нещо.

Важна забележка: Ако не сте сигурни, че разбирате адекватно всички случаи, в които се използва приложението, вероятно ще пропуснете необходимите сценарии за тестване. Препоръчвам ви да се консултирате с продуктовия мениджмънт и да прегледате Google Analytics или други показатели за регистриране, за да получите пълна представа за различните видове заявки, с които вашата система работи.

Тестване на моментна снимка

Когато създавате предпазна мрежа, вие търсите „тестове за закрепване“, които закрепват текущото поведение на системата (правилно или неправилно) на място, така че да сте изрично наясно с всяка промяна, която правите в поведението на кода.

Техника номер едно, която препоръчвам за бързо създаване на такъв тест, е използването на моментно тестване.

Тестването на моментна снимка сравнява обект с предишна моментна снимка и е неуспешно, ако бъде открита някаква промяна. Ако искате да видите някои подробности, вижте моята статия относно използването на Jest за JavaScript / TypeScript тестове за фиксиране, както и Snapper за .NET тестове.

Тестване на UI

Тестването на потребителския интерфейс включва автоматизиране на взаимодействието с потребителския интерфейс на приложението. Може да се направи за уеб, настолни и мобилни приложения и ви позволява автоматично да тествате система от гледна точка на крайния потребител. Различните инструменти и техники се различават в зависимост от езиците и платформите, въпреки че Selenium е изключително популярна опция за уеб тестване.

цигулар

Ако вашият код прави HTTP извиквания към други системи, може да е удобно да видите честотата и съдържанието на тези повиквания. Fiddler действа като прокси, което прихваща изходящия трафик и ви позволява да инспектирате повикването и неговия отговор. По този начин можете да наблюдавате точните подробности за повикванията, направени от приложението, за да определите дали поведението се променя.

Пощальон

От другата страна на разработката на API, можете да използвате инструмент като Пощальон, за да направите повиквания към вашето приложение (или всяко друго) и да проверите отговора. Освен това Postman има функции за изпълнение на набори от повиквания в група или по график и за извършване на някои основни твърдения около върнатите стойности на REST повикванията. Това позволява на неразработчиците да използват Postman, за да напишат набор от интеграционни тестове.

Регресионно тестване

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

Подобрете възможността за тестване

Сега, след като имаме някои основни тестове от край до край, за да проверим дали няма случайно да променим поведението, нека направим аспектите на приложението по-лесни за тестване изолирано чрез тестове на модули.

Намалете плътното свързване

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

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

Това прави много по-лесно писането на тестове, които тестват само въпросната логика и изолират действителния тестван код.

Този процес се нарича инверсия на контрола (IoC) или инжектиране на зависимост, защото вместо класът да е тясно свързан с това, което извиква, зависимостта се инжектира в класа чрез параметър, свойство или аргумент на конструктора.

Заобикаляне на ограниченията на базата данни

Ако искате да тествате базата данни или разделянето на обажданията към базата данни е твърде трудно за извършване наведнъж, може да се наложи да направите някаква специална логика за настройка с базата данни.

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

Причината, поради която искате да направите тази логика преди изпълнението на тестовете, вместо след това, е, че ако тестът е неуспешен, данните може да са в невалидно състояние и ще се провалят при следващото изпълнение, дори ако вашият код е правилно внедрен.

.NET има някои интересни възможности, вградени в Entity Framework, които ви позволяват да работите с база данни в паметта. Това позволява на вашия код все още да работи с контексти на базата данни в името на тестването, но съдържанието на тази база данни зависи изцяло от вашия контрол по време на теста.

Подобрете вашите тестове

С нарастването на повече тестове е важно да се уверите, че тези тестове са поддържаеми, ясни, лесни за четене и ефективни.

Тъй като фокусът на тази статия е върху намаляването на риска при извършване на промени, а не върху почистването на тестовия код, ще пропуснем това, но ако сте любопитни как изглежда процесът на прогресивно подобряване на модулните тестове, препоръчвам ви да прочетете моята статия за рефакторинг на C# модулни тестове

Преработете кода

Сега, когато имате модулни тестове, които имат адекватна степен на покритие и простота, трябва да се чувствате комфортно да правите промени. Ако не се чувствате комфортно, вероятно ще трябва да се върнете и да добавите по-разнообразни тестове или да се консултирате със старши член на екипа, докато можете уверено да заявите, че вашият план за намаляване на риска покрива адекватно работата, която трябва да свършите.

Няма да говоря за конкретната механика на промяна на кода, тъй като те варират в зависимост от типа проекти, езици, инструменти, технологии и от вкуса на техническия дълг, за разрешаването на който работите.

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

Използвайте интелигентни инструменти

Повечето съвременни среди за разработка имат вградени инструменти за рефакторинг. Те предлагат общи операции като извличане на променлива, изтегляне на метод до базов клас (или дефиниция на интерфейс), извличане на нов метод от по-голям метод и т.н.

Горещо ви препоръчвам да намерите инструменти, на които имате доверие и които могат да автоматизират тези процеси, защото те са много по-малко склонни да направят грешка от вас и да бъдат разсейвани или прекъсвани не е слабост, от която страдат.

Аз лично харесвам разширението на Visual Studio ReSharper и пакета от продукти JetBrains, обикновено наричан семейство редактори IntelliJ (въпреки че те обслужват много езици), но вие трябва да намерите инструменти, на които лично имате доверие и от които получавате стойност.

Флагове за функции

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

Обикновено започвам с приложението, което насочва целия трафик към новата версия на преработената логика, но мога да задам запис за конфигурация някъде при спешен случай и приложението ще се върне към използването на наследената реализация, без да се налага да променя или разгръща код.

След като бъде потвърдено, че кодът работи според очакванията, можете да премахнете флага на функцията и наследеното внедряване от бъдеща версия.

Учен

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

Различното при Scientist е, че той винаги ще връща върнатия резултат от наследената версия на повикващия, но ще сравнява резултатите от старата и новата реализация, за да види дали са се променили. Ако са го направили, Scientist ви позволява да регистрирате този инцидент, където пожелаете.

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

Вижте моята статия за Scientist за повече информация.

Разширете вашите тестове

Сега, когато се чувствате комфортно и сте готови да започнете да плащате технически дълг, вероятно забелязвате места, където тестването е слабо. Възползвайте се от тази възможност, за да разширите тестовете по възможно най-чистия начин, тъй като това само ще улесни живота ви.

Ето няколко бързи идеи на високо ниво за разширяване на тестовете:

Параметризирани модулни тестове

Много рамки за тестване на модули ви позволяват да напишете един метод за тестване, който приема параметри от различни параметризирани случаи на тестове на модули. Това поддържа тестовия код прост и поддържаем.

Изискване на тестове по време на преглед на кода

Въпреки че не е промяна на кода, едно от нещата, които намирам за полезни, е да предизвикам членовете на екипа си по време на преглед на кода и да настоявам нещата да носят необходимите тестове на единици, когато има смисъл да се направи това.

Предоставяне на проекти на тестови планове на QA

Друго нещо, което обичам да правя по време на преглед на кода, е да прегледам чернова на планове за тестване, които разработчикът е написал. Това може да е нещо от кратко изречение за това как да се тества функцията до официален запис в система за проследяване на тестови случаи.

Идеята е, че осигуряването на качеството може да вземе това предложение за тестов случай и да го приеме или премахне, след което да коментира тестовите случаи, които разработчикът не е идентифицирал, и да подреди тестовия случай. Това помага на QA да се ориентира за това какво представлява новата функция (като има проект на тестов случай) и помага на разработчиците да мислят повече като тестери, като им дава обратна връзка от QA относно техните тестови планове.

Развитие, водено от поведението

И накрая, съществуват инструменти за разработка, управлявана от поведението (BDD) и работни потоци, за да позволят на собствениците на продукти и членовете за осигуряване на качеството да пишат тестове на единици на обикновен английски, а на разработчиците да кодират специални функции за съвпадение, за да интерпретират тези команди и да изпълняват тестове.

Предимството на това е, че сега имате по-голямо разнообразие от хора, които пишат тестови случаи за вашето приложение, като помагат за качеството като цяло и изясняват, че тестването на качеството и единиците не е само отговорност на разработката или QA, но и на целия екип.

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

Надявам се, че тази статия ви е дала някои нови неща, които да имате предвид при запазване на качеството на приложението по време на рефакторинг и след това.

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

Първоначално публикувано на https://dev.to на 5 октомври 2019 г.