„Да криввам“ или „да подпирам с патерици“ — означава да се създаде временно решение на проблем, което затваря бъг или добавя функционалност, но не премахва първопричината и не отговаря на архитектурните стандарти на проекта. Патериците са неизбежни във всяка разработка: крайни срокове, непълно разбиране на системата и външни ограничения принуждават да се вземат компромисни решения. Според Refactoring Guru, ключовата разлика между прагматична патерица и технически дълг — в осъзнатостта на решението и наличието на план за нейното премахване. Правилното използване на временни решения изисква дисциплина и документиране.
Основни моменти
Патерица (crutch) — софтуерно решение, което работи, но е направено „набързо“: затваря конкретен проблем, но не премахва причината му, не следва архитектурата на проекта и може да се счупи при най-малките промени в средата. Метафората е точна — като истинска патерица, такъв код помага да се „ходи“, но не лекува „крака“.
Разработчиците „подпират с патерици“ бъгове, несъвместимости на версии, характеристики на платформа и спешни изисквания на клиента. Типична патерица — патерица-условие: ако iOS 15, добави разстояние; ако Huawei — скрий бутона. Такива проверки се умножават и превръщат кода в „слоиста торта“ от платформени и версионни разклонения.
Патериците могат да бъдат различен мащаб: от един ред с патерица-условие до цял междинен модул, който „поправя“ поведението на библиотека. Важно е да разберем, че патерицата не винаги е лошо нещо: в правилните ръце тя е инструмент, позволяващ пускането на продукта навреме. Проблемът започва, когато патерицата остане в кода завинаги.
Основната причина за появата на патерици е конфликтът между идеалното решение и реалните ограничения на проекта. Разработчикът знае как да го направи правилно, но времето, парите или техническите ограничения не позволяват. В резултат се появява компромисно решение, което „просто работи“.
Нека разгледаме четири основни причини, поради които разработчиците съзнателно прибягват до патерици. Разбирането на тези причини помага да гледаме на патериците не като на грешка, а като на прагматичен инструмент, който се нуждае от управление.
Най-честата причина. Пускането е утре, бъгът се възпроизвежда само на конкретен модел, архитектурното поправяне отнема две седмици. Патерицата-условие отнема един час и затваря проблема. След пускането екипът обещава да се върне и да препише правилно. „Няма нищо по-постоянно от временното“ — точно за такива патерици.
Библиотека A изисква Android 12, но вашето приложение поддържа Android 10. Решението — да се напише междинен слой, който проверява версията на ОС и избира пътя на изпълнение. Това е патерица, защото при актуализиране на библиотеката междинният слой ще трябва да се препише. Но алтернативата — отказ от библиотеката или поддръжка на стари устройства — може да бъде по-лоша.
// Патерица за съвместимост с API 29
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
useModernApi()
} else {
useLegacyFallback()
}
Библиотеката, от която проектът зависи, съдържа бъг, но нейното актуализиране може да отнеме седмици (нужен е PR, код-ревю, публикуване). Вместо да чака, екипът пише wrapper, който коригира поведението на библиотеката в движение. След излизането на коригираната версия на библиотеката, wrapper се премахва. Ако не се премахне — това вече е архитектурен проблем.
Нов разработчик в legacy проект не разбира защо кодът работи точно така. Вместо да разбере, той добавя ново условие върху съществуващото. Това е най-опасният тип патерица, защото авторът не осъзнава, че това е патерица. Единственото лекарство — код-ревю и програмиране по двойки за нови членове на екипа.
Не всяка патерица е лошо нещо. В реалната разработка абсолютната чистота на кода е недостижима и често нецелесъобразна. Прагматичният подход признава, че временните решения са част от процеса, но изисква тяхната осъзнатост, документиране и планиране на премахването. Патерицата е оправдана, когато решава бизнес задача по-бързо от чисто архитектурно решение.
Критерии за оправдана патерица: затваря конкретен проблем, има собственик (кой отговаря за премахването ѝ) и съществува план за рефакториране. Ако поне едно от трите условия не е изпълнено — патерицата се превръща в технически дълг. Инструменти като TODO-коментари с тикет в тракера — минимален начин за документиране.
Критичен бъг в release клона, който трябва да бъде затворен до утрешното внедряване. Чистото решение изисква рефакториране на архитектурата и ще отнеме две седмици. Патерица — добавете проверка за nil и изпратете поправката като hotfix. Условия за оправданост: в тракера е създаден тикет за рефакториране, определена е отговорна личност, патерицата е маркирана с коментар. След две седмици екипът се връща към задачата.
// TODO: IT-1234 — премахнете тази патерица след рефакториране на AuthService
guard let userId = session.user?.id else {
return Result.failure(.notAuthenticated)
}
Границата между осъзната патерица и архитектурен проблем (технически дълг) преминава през два параметъра: осъзнатост на решението и наличие на план за премахване. Патерицата винаги е временно решение с известен срок на живот. Техническият дълг — последица от множество патерици, оставени без внимание.
| Параметър | Осъзната патерица | Технически дълг |
|---|---|---|
| Осъзнатост | Екипът знае, че това е временно решение | Никой не помни защо кодът е такъв |
| Документация | Има TODO, тикет в тракера | Няма коментари, връзки, описание |
| План за премахване | Определен sprint за рефакториране | „Някой ден ще препишем“ |
| Влияние | Локално, не пречи на нова функционалност | Блокира промени, забавя разработка |
Ситуацията се влошава, когато броят на патериците надхвърли критичната маса. Всяка нова патерица увеличава „крехкостта“ на системата: промяна на едно място чупи друго. В резултат разработката се забавя, бъговете се умножават, а нов разработчик не може да разбере кода без помощта на автора. В този момент патериците престават да бъдат временни решения и се превръщат в архитектурен проблем.
Ако в кода има пет вложени проверки за версия на ОС, производител на устройство и наличие на конкретна библиотека — това не е патерица, а архитектурен проблем. Ако добавянето на една поправка причинява три регресии в съседни модули — патериците са престанали да бъдат локални. Ако код-ревюто редовно се отхвърля поради „още една патерица“ — време е да планирате рефакториране.
Рефакториране на патерици — процес на замяна на временни решения с архитектурно правилни. Това изисква време, затова е необходима стратегия за приоритизиране: не всички патерици трябва да бъдат премахнати незабавно. Добра стратегия — оценявайте всяка патерица по два параметъра: честота на промени в тази област на кода и влияние върху потребителите.
Висок приоритет — патерици в често променяни модули (бизнес логика, UI с общо предназначение), които забавят разработката и причиняват регресии. Среден приоритет — патерици в рядко променяни модули, но с потенциално влияние върху потребителите (обработка на плащания, оторизация). Нисък приоритет — патерици в legacy код, който работи стабилно и не е планиран за модификация.
Стъпка 1: инвентаризация — намерете всички TODO и FIXME, свързани с патерици. Стъпка 2: оценка — определете кои все още са актуални. Стъпка 3: планиране — назначете рефакториране на патерици в sprint, започвайки от високоприоритетните. Стъпка 4: замяна — имплементирайте чистото решение, премахнете патерицата и нейния TODO-коментар. Стъпка 5: верификация — уверете се, че тестовете преминават и няма регресии.
# Намерете всички TODO-патерици в проекта
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.kt" --include="*.swift" .
Най-добрият начин за борба с патериците е да не ги създавате без необходимост. Преди да напишете патерица, задайте си три въпроса: може ли да се направи чисто решение в разумен срок? Има ли алтернатива, която не е патерица? Ще има ли екипът време да се върне и да препише това? Ако поне на един въпрос отговорът е „не“ — помислете отново, преди да „подпрете“ кода.
Често задавани въпроси
Да криввам — да напиша временно решение, което затваря проблема, но не премахва причината. Кодът работи, но не отговаря на архитектурата на проекта и може да се счупи при промени.
Патерица — осъзнато временно решение с план за премахване. Технически дълг — последица от много забравени патерици. Патерицата е локална, дългът е системен и блокира разработката.
Когато крайният срок е критичен, чистото решение изисква време и патерицата е документирана с TODO-коментар и тикет в тракера. Условие: патерицата има план за премахване в обозримо бъдеще.
Добавете TODO или FIXME с номера на тикета и кратко описание на правилното решение. Пример: // TODO: IT-567 — пренапишете, използвайки Factory pattern. Без тикет патерицата ще бъде забравена.
Направете инвентаризация на всички TODO, оценете приоритета, започнете от често променяни модули. Заменете патерицата с чисто решение, премахнете коментара и проверете с тестове.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също