Підтримка й ефективність

Технічний борг: як побачити ціну майбутніх змін

Робоча система може приносити цінність і водночас ускладнювати наступну зміну. Наприклад, команда запускає імпорт вручну, бо даних поки мало; згодом…

Глава 66Заморожене українське видання

Робоча система може приносити цінність і водночас ускладнювати наступну зміну. Наприклад, команда запускає імпорт вручну, бо даних поки мало; згодом кожен новий формат потребує окремої інструкції, а підтримка витрачає на нього години. Сам факт, що система неідеальна, ще не робить це технічним боргом. Потрібно знайти конкретний компроміс або обмеження, показати його наслідок і вирішити, чи варто змінювати його тепер.

Навчальний результат. Читач може відрізнити технічний борг від дефекту, застарілого стилю чи особистої переваги; описати симптом, причину, вплив і докази; оцінити варіанти з огляду на контекст продукту; прийняти обґрунтоване рішення — сплатити частину боргу, зменшити його, спостерігати або поки залишити без змін.

66.1. Метафора пояснює витрати, але не рахує їх

Технічний борг — метафора для раннього технічного компромісу або накопиченого обмеження, яке дає користь тепер, але робить майбутню зміну дорожчою чи ризикованішою. Вард Каннінґем використав її, пояснюючи, чому незріле рішення може допомогти швидше випустити продукт, а нерозібране з часом заважає його розвивати. Він порівнював додаткові зусилля на зміну такого рішення з відсотками за боргом. Каннінґем, звіт про WyCash

У метафорі можна розрізнити:

Фінансова метафора У програмному продукті
Тіло боргу Обсяг роботи, потрібний, щоб усунути або обмежити конкретне технічне обмеження
Відсотки Додатковий час, повторні дефекти, ручні кроки чи ризик під час наступних змін у цій ділянці
Вигода від позики Час або інша цінність, отримана завдяки рішенню, яке спростило чи відклало роботу зараз
Погашення Обмежене поліпшення, поступова заміна, видалення непотрібної функції або усунення причини

Це не бухгалтерський баланс. Для кожного пункту не існує достовірної універсальної «ставки», яку можна вивести з кількості рядків, порушень стилю чи віку коду. Оцінки часу й майбутньої вигоди приблизні; припущення, майбутній попит і пріоритети можуть змінитися. Навіть оцінка тіла боргу — не завжди одна разова сума: потрібну заміну можуть доводити кількома релізами. Мартін Фаулер прямо застерігає, що метафора позики має межі: на відміну від банку, програмний борг часто «нараховує відсотки», коли команді доводиться змінювати саме цю ділянку. Тому стабільний код, якого продукт більше не торкається, може не потребувати погашення. Фаулер, «Technical Debt»

Не перетворюйте це на виправдання недбалості: «колись приберемо» без причини, власника й умови перегляду не є усвідомленим компромісом. І не плутайте естетичне невдоволення з доведеними витратами: «мені більше подобається новий фреймворк» ще нічого не каже про користь для продукту.

66.2. Розділіть компроміс, помилку і смак

Рішення про борг починається з класифікації проблеми. Різні причини вимагають різної реакції.

Ситуація Що вона означає Як із нею працювати
Відомий тимчасовий компроміс Команда спростила рішення заради конкретної близької вигоди й розуміє майбутню ціну Записати вигоду, обмеження, відповідального й умову перегляду
Обмеження, помічене згодом Нові вимоги або досвід показали, що попередня модель чи структура заважає Виміряти теперішній вплив; не звинувачувати попередню команду заднім числом
Дефект Реальна поведінка порушує вимогу або контракт Виправити дефект із пріоритетом за наслідком; окремо перевірити, чи повторювану причину становить структура системи
Застарілий стиль або інструмент Є новіша версія чи мода, але вплив на продукт ще не показано Перевірити підтримку, сумісність, ризики й майбутні потреби; не змінювати лише заради новизни
Смак рецензента Комусь не подобається назва, формат чи архітектурний напрям без помітної шкоди Розглядати як пропозицію, доки не наведено приклад витрат або ризику

Фаулер пропонує розглядати усвідомленість рішення окремо від того, чи було воно обачним: команда могла свідомо взяти ризик, несвідомо успадкувати його або виявити правильніший підхід уже після роботи з продуктом. Це корисна лінза для обговорення, а не стандарт, за яким треба ставити оцінки чи автоматично приймати кожен компроміс. Зокрема, те, що команда назвала рішення «тимчасовим», не робить його обачним: вигода могла бути нечіткою, а витрати — недооціненими. Фаулер, «Technical Debt Quadrant»

Дефект і борг можуть бути пов’язані, але їх не слід зливати в одну задачу без причини. Якщо рахунок у замовленні неправильний, це дефект, який слід виправити. Якщо помилка повторюється, бо правила ціни скопійовані в кілька місць, дублювання може бути окремим технічним обмеженням, що підвищує ризик нових дефектів. Перше питання — «чи правильно працює система?», друге — «чи робить її структура наступну зміну невиправдано складною або небезпечною?»

66.3. Назвіть наслідок, а не лише неприємне відчуття

Технічне обмеження має практичне значення, якщо воно створює повторювану ціну, знижує надійність або блокує потребу продукту. Це може бути:

  • зміни в одному правилі вимагають синхронно правити кілька копій;
  • тести нестабільні або не перевіряють важливі наслідки, тому команда витрачає час на ручну перевірку;
  • реліз потребує повторних ручних кроків, які легко пропустити;
  • одна операційна помилка може пошкодити дані або зупинити критичну функцію;
  • контракти між компонентами залежать від прихованих домовленостей і часто ламаються;
  • знання про розгортання, інтеграцію або відновлення зосереджене в однієї людини;
  • зовнішню залежність неможливо оновити в межах потрібного строку підтримки чи сумісності.

Неприємний код може не мати такого наслідку, якщо він стабільний і не входить у майбутню роботу. І навпаки, короткий ручний крок може бути значним боргом, якщо він регулярно затримує реліз або під час нього губляться дані. Якщо проблема створює безпосередній ризик безпеці, конфіденційності чи цілісності даних, не ховайте її в необмеженому списку «техборгу»: потрібні відповідальний за ризик, строк перегляду та тимчасовий захист, доки команда готує зміни.

66.4. Не приписуйте інструментам складність самої задачі

Фред Брукс розрізняв сутнісну складність — роботу, пов’язану з правилами й поняттями самої предметної задачі — та випадкову складність, яку додають спосіб реалізації, мова, інструменти чи обмеження середовища. Його розрізнення не є класифікатором технічного боргу: воно допомагає побачити, яку частину труднощів можна зменшити кращим інструментом або організацією, а яка походить із самої задачі. Брукс, «No Silver Bullet», технічний звіт Університету Північної Кароліни, 1986

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

Так само архітектура не буває боргом за назвою. Моноліт може бути простим і відповідати потребам невеликого продукту; розподіл на сервіси може додати мережеві виклики, керування контрактами й операційну роботу. У міру зростання навантаження, команди чи продуктових вимог цей компроміс може перестати бути прийнятним. Книжка Software Architecture: The Hard Parts наголошує на аналізі варіантів у конкретному контексті: той самий поділ на компоненти змінює баланс між автономністю, координацією, зв’язністю та витратами супроводу. Автори застерігають від вибору «переможця» лише за підрахунком абстрактних плюсів і мінусів. О’Райлі: Software Architecture: The Hard Parts; обговорення авторів у Thoughtworks

66.5. Реальні системи змінюють ціну компромісу

Досвід мережі піцерій Dodo Pizza, описаний на Agile Alliance, показує, чому рішення треба оцінювати відносно навантаження й процесу доставки. Окремий ранній епізод автор Dodo Engineering описує на Habr: популярний допис генерального директора змусив блог щоразу запитувати головну базу про виторг усієї мережі, через що каси ресторанів перестали отримувати відповіді. Команда швидко додала репліку бази лише для читання, але не мала плану технічного переосмислення. Під час рекламної кампанії 2018 року навантаження зросло приблизно зі 100 до 300 замовлень за хвилину; система досягла меж вертикального масштабування, а база перестала обробляти запити. До цього продукт швидко зростав, але мала кількість автоматизованих перевірок і ручні регресійні перевірки подовжували релізи.

Команда зупинила більшість продуктової розробки заради технічних змін. Планували три місяці, але робота тривала близько року; автор звіту зазначає, що за цей час команди випустили одну значну бізнес-функцію. Вони автоматизували розгортання, поступово відокремили найнавантаженіший компонент, додали чергу подій та інвестували в автоматичні й продуктивнісні перевірки. Попри рік роботи, під час наступної масштабної кампанії система знову падала й перезавантажувалася кожні три години; автор пов’язує кризу з великими втратами бізнесу. Команда продовжила роботу над асинхронною обробкою, перевірками навантаження та журналами; звіт описує кращу стабільність у 2019 році.

Це досвід одного продукту, а не доказ, що моноліти самі по собі є боргом або що річна зупинка розробки підходить іншим командам. Він показує ціну відкладання роботи за умов конкретного росту, а також межі великого «погашення»: зміна структури не гарантувала стійкості до нового навантаження, тож команді знадобилися окремі дані про продуктивність і зміни процесу. Грошову оцінку понад 1,5 мільйона доларів наводить автор звіту; її варто читати як його оцінку витрат, а не як незалежний аудит. Agile Alliance: «Technical debt leading to a company crisis»; Dodo Engineering: оригінальна публікація звіту на Habr

Shopify описувала інший шлях для свого великого моноліту: поступово створювати межі компонентів і їхніх контрактів, залишаючи систему монолітом. Попередній інструмент збирав під час тестів у середовищі безперервної інтеграції (continuous integration, CI) граф фактичних викликів методів між компонентами. Ці дані були шумними, а повний аналіз тривав понад годину. Статичний аналіз залежностей Packwerk працював за хвилини й на час публікації статті вже був інтегрований у процес запитів на злиття: він відхиляв зміни, що порушували граф залежностей або інкапсуляцію компонентів. Shopify тоді застосовувала Packwerk приблизно до третини компонентів головного моноліту, тож перевірки ще не охоплювали весь код. Компанія також описала компроміс між тимчасовим групуванням відповідальностей із призначеними власниками та повністю відокремленими компонентами, які легше зрозуміти, але які можуть залишити решту системи незмінно заплутаною. Це інженерний звіт самої компанії про її контекст, а не універсальна рекомендація. Він показує, що поступове поліпшення моноліту може бути осмисленішим за негайне розділення на сервіси. Shopify Engineering: «Under Deconstruction: The State of Shopify’s Monolith»

66.6. Описуйте борг як перевірювану проблему

Запис «переписати модуль красиво» не допомагає спланувати роботу. Інша людина не знає, що болить, кому це заважає, що є прийнятним результатом і коли повернутися до питання. Замість цього опишіть конкретну ситуацію:

Поле Запитання
Симптом Що регулярно сповільнюється, ламається або потребує ручної роботи?
Причина Яке рішення, дублювання, залежність чи пропущений процес це спричиняє? Що відомо, а що лише припускаємо?
Вплив і докази Які зміни, інциденти, повторні помилки, кроки чи затримки це підтверджують?
Власник Хто відповідає за предметну ділянку та перегляд рішення?
Варіанти Чи можна прийняти ризик, спростити локально, замінити частину, видалити непотрібне або змінити процес?
Вартість і ризик змін Які сумісність, дані, користувачі, тести, релізи й відновлення зачепить кожен варіант?
Тригер перегляду Яка зміна попиту, навантаження, підтримки чи ризику покаже, що час повернутися до питання?

Не вигадуйте точну причину, якщо доказів поки немає. У зверненні №14010 про DefectDojo було описано регресію після версії 2.53.0: раніше пошук перевіряв усі відповідні знахідки, а після зміни розглядав лише перший збіг. Якщо перший запис уже був пом'якшений, функція позначення дубліката зупинялася на помилці й не переходила до наступного кандидата. Дублікат №14012 додав відтворення та журнал, де система знаходила 30 збігів, але залишала новий запис окремою активною знахідкою. Його автор припускав, що це наслідок рефакторингу; сторінка подає це як можливу причину, а не встановлений ланцюг історичних змін. Це був дефект, а не необов'язковий борг: запит на злиття (pull request, PR) №14011 виправив перебір кандидатів і був злитий у гілку bugfix 4 січня 2026 року; сам PR зазначає, що виправлення не стосується Pro, де була окрема робота. DefectDojo Issue №14010; дублікат №14012; PR №14011

66.7. Порівнюйте ризик і майбутню роботу

Для пріоритизації боргу важливі не його вік і не кількість порушень стилю, а наслідок у контексті планів продукту. Запитайте:

  • як часто команда вже змінює цю ділянку і чи повернеться до неї за майбутнім планом;
  • скільки часу, повторних перевірок або ручних дій додає обмеження;
  • які помилки можуть виникнути та кого чи що вони зачеплять;
  • чи стосується це безпеки, доступу до даних, фінансових результатів або цілісності записів;
  • яка вартість і ризик самого втручання, включно із сумісністю, міграцією та відновленням;
  • скільки часу продукт імовірно ще житиме та які інші зміни можуть змінити передумови.

Критичний ризик для даних може вимагати негайного обмеження чи виправлення, навіть якщо код рідко змінюється. Низький вплив у функції, яку планують видалити за місяць, може зробити повну заміну невиправданою. Рідко змінюваний модуль не варто переробляти лише тому, що він потворний, але його небезпечно ігнорувати, якщо він містить активну вразливість або заважає обов’язковій міграції.

Зважуйте вартість усунення, ризик зміни, уникнуті майбутні витрати й інші заплановані роботи разом. Не зводьте всі ці чинники до універсального бала «скільки боргу» — два відділи можуть по-різному оцінити той самий пункт через різну частоту змін, ціну помилки й горизонт продукту. Рішення про відкладення теж має містити причину та дату чи подію повторного перегляду.

66.8. Виберіть обсяг дії та спосіб перевірити користь

Погашення не обмежується великим переписуванням. Залежно від доказів команда може:

  1. Прийняти й спостерігати. Записати причину, власника, тимчасовий контроль і тригер повернення до питання.
  2. Спрощувати локально. Усувати зайві кроки чи дублювання там, де команда вже працює, якщо це реально знижує майбутні витрати.
  3. Замінювати поступово. Змінювати одну межу або частину системи за раз, коли сумісність і дані не дозволяють безпечної одноразової заміни.
  4. Видалити невикористане. Виміряти, чи функція справді потрібна, а тоді прибрати її разом із зайвою підтримкою.
  5. Змінити процес. Додати автоматичну перевірку, власника контракту, генерацію чи іншу повторювану домовленість, якщо проблема походить не лише з коду.
  6. Переосмислити значну частину. Порівняти варіант із локальними поліпшеннями за доказами. Повне переписування — дорога гіпотеза з новими помилками переходу та невідомими, а не синонім «сплатити борг».

Визначте до зміни, що має поліпшитися: менше часу на типовий запит, менше регресій, один ручний крок замість кількох, коротший перегляд змін або передбачуване відновлення. Після змін перевірте саме цей результат і його побічні ефекти. Для архітектурного вибору запишіть рішення та умову перегляду в записі архітектурного рішення (Architecture Decision Record, ADR); конкретну роботу ведіть у відстежуваному беклозі, а вплив на випуск — у плані релізу.

Приклад 1. Ручний імпорт перестав бути дешевим (умовний сценарій)

На початку продукт приймає невелику кількість табличних файлів від кількох партнерів. Адміністратор вручну перевіряє формат і запускає імпорт раз на тиждень. Для цього етапу автоматичний конвеєр з відновленням і повторною обробкою може коштувати дорожче за процес.

Коли партнерів стає більше, підтримка виконує імпорт кілька разів на день, а неповні рядки щоразу виправляє по-різному. Команда фіксує час на ручну обробку, повторні звернення через відхилені рядки й випадки, де повторний запуск створює дублікати. З цих спостережень випливає обмежена задача: додати перевірку формату до збереження даних і зробити повторний імпорт безпечним для вже оброблених записів; залишити систему відновлення наявною, а не переписувати весь конвеєр.

Власник імпорту погоджує критерії: підтримка більше не змінює прийнятні файли вручну, причину відхилення можна знайти за конкретним рядком, а повторний імпорт не створює ще один запис. Для ілюстрації можливого результату, а не як фактичні дані: за чотири тижні після випуску ручна робота скоротилася з чотирьох годин до 45 хвилин на тиждень, а повторні імпорти не створили дублікатів у перевірених запусках. Команда також перевіряє помилкові відхилення й відновлення після збою. Якщо партнерів мало, імпорт рідкісний, а помилковий запуск легко скасувати, автоматизацію варто було б відкласти або зробити простішою.

Приклад 2. Агент знайшов новішу бібліотеку (умовний сценарій)

Агент бачить, що проєкт використовує попередню версію бібліотеки і пропонує замінити її на новішу, бо вона «сучасніша». Це поки не доказ технічного боргу. Команда перевіряє, чи підтримується чинна версія, чи потрібна функція відсутня, чи є реальний ризик для сумісності та які тести підтвердять поведінку після переходу.

Виявилося, що чинна версія підтримується, система викликає її лише в рідко змінюваному внутрішньому інструменті, а новіша потребує зміни конфігурації та залежних модулів. Команда залишає версію як є, додає до запису дату завершення її підтримки як тригер перевірки й не бере на себе ризик переходу зараз. Умовний результат квартального перегляду: підтримка ще триває, потрібної функції не бракує, інцидентів через бібліотеку не було — команда не витратила час на міграцію без помітної користі. Якщо перевірка виявить непідтримувану версію з відомою активною вразливістю або блокуванням потрібного оновлення, рішення зміниться; тоді це вже конкретний ризик, а не перевага новизни.

Агент корисний як спосіб знайти різницю та запропонувати варіанти, але його висновок має посилатися на конкретну версію, спосіб використання, джерело ризику й обсяг змін. Не дозволяйте завданню «оновити бібліотеку» автоматично перерости в заміну всього шару доступу до даних. Окремий дефект або термінове виправлення безпеки також не слід обтяжувати необов’язковим переписуванням суміжних модулів.

Приклад 3. Копії правила ціни спричинили різні рахунки (умовний сценарій)

Умовна крамниця застосовує знижку і в кошику, і під час створення рахунку. Після зміни правила покупець бачить одну суму до оплати, але іншу в рахунку. Команда виправляє неправильну суму як дефект. Перевірка історії змін показує, що ті самі правила реалізовані в двох місцях і двічі спричиняли схожі помилки; це вже доказ окремого структурного обмеження.

Замість загальної вимоги «переписати розрахунок кошика» команда обмежує подальшу роботу єдиним джерелом правила та критеріями перевірки: однаковий набір товарів дає ту саму ціну в кошику й рахунку; крайові значення знижок не змінюють результат між екранами; журнал показує версію застосованого правила. Умовний результат першого місяця після випуску: вибірка замовлень не показала розбіжностей між кошиком і рахунком, а наступне погодження правила потребувало однієї зміни в єдиному місці замість синхронізації двох копій. Команда стежить за обома показниками й побічними ефектами. Якщо дві гілки відповідають різним чинним правилам і так задумано, об’єднувати їх лише через зовнішню схожість не слід.

Як саме безпечно змінювати таку реалізацію — окрема тема наступної глави про рефакторинг. Тут важливо спершу довести проблему й обмежити мету, щоб корисне виправлення дефекту не перетворилося на незаплановану перебудову всієї крамниці.

66.9. Відстежуйте ціну, а не декоративний бал

Корисні спостереження залежать від проблеми. Для імпорту підійдуть хвилини ручної роботи й відсоток файлів, що потребують переробки; для обмеження тестів — тривалість перевірки, нестабільні запуски й регресії; для операційного ризику — невдалі відновлення, кроки втручання та інциденти; для зміни меж компонентів — скільки компонентів змінюється разом у типових задачах.

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

66.10. Закріпіть рішення й перегляньте його за умовою

Для довготривалого компромісу запишіть у ADR контекст, чинники вибору, прийняте рішення, відомі наслідки й подію, за якої його треба переглянути. Наприклад: «Ручний імпорт прийнятний, доки він виконується не частіше одного разу на тиждень і займає до 30 хвилин; при зростанні партнерів або повторному інциденті команда оцінює автоматизацію». Це не зобов’язання автоматично почати переписування, а спосіб помітити, що змінилися початкові умови.

Короткий локальний пункт може жити в беклозі поруч із функціями, яких він стосується. Під час планування змін у цій ділянці команда може частково зменшити борг, якщо це безпечно і виправдано наступною роботою. Після релізу перевірте критерії успіху, сумісність, дані й операційну поведінку. Якщо ви вирішили поки не платити, залиште власника рішення й дату або умову перегляду; тиша в списку задач — не рішення.

Джерела та подальше читання

Сторінки й технічні звіти перевірено 25 вересня 2026 року; звіт Agile Alliance і його оригінальну публікацію Dodo Engineering на Habr повторно перевірено 10 жовтня 2026 року. Для історичних матеріалів рік публікації вказано окремо; кількісні результати досвіду Dodo Pizza наведено як свідчення автора звіту, а не незалежну перевірку.

  • Ward Cunningham, The WyCash Portfolio Management System, досвідницький звіт OOPSLA (Object-Oriented Programming, Systems, Languages, and Applications — об’єктно-орієнтоване програмування, системи, мови й застосунки), 1992 року — первинний опис метафори боргу, вигоди ранньої поставки й ціни відкладеної консолідації.
  • Martin Fowler, Technical Debt, перегляд 2019 року — пояснення метафори, умовної вартості змін і меж кількісної оцінки.
  • Martin Fowler, Technical Debt Quadrant, 2009 — розрізнення усвідомленого й неусвідомленого, обачного й необачного боргу; використовується тут як розмовна рамка, не шкала оцінювання.
  • Frederick P. Brooks Jr., No Silver Bullet: Essence and Accidents of Software Engineering, технічний звіт Університету Північної Кароліни, вересень 1986 року — першоджерело розрізнення сутнісної та випадкової складності.
  • Neal Ford, Mark Richards, Pramod Sadalage та Zhamak Dehghani, Software Architecture: The Hard Parts, O’Reilly, 2021; розмова авторів у Thoughtworks — аналіз архітектурних компромісів у контексті та межі універсальних матриць «за і проти».
  • Agile Alliance, Technical debt leading to a company crisis — досвідницький звіт команди Dodo Pizza про зростання навантаження, відкладені зміни й операційні наслідки; дата публікації на перевіреній сторінці не зазначена.
  • Philip Müller, Under Deconstruction: The State of Shopify’s Monolith, Shopify Engineering, 16 вересня 2020 року — інженерний звіт про поступову модульність, компроміси меж компонентів і перенесення перевірки залежностей ближче до змін.
  • DefectDojo, Issue №14010: Regression: deduplication stops when an inactive/mitigated candidate has been found, закрите звернення від 31 грудня 2025 року; дублікат №14012; PR №14011, злитий 4 січня 2026 року — відтворений, згодом виправлений дефект; приклад розділення спостережуваного симптому від можливої історичної причини.