Костил (енгл. workaround, kludge, hotfix) — је привремено или неоптимално решење проблема у коду које ради, али крши принципе чисте архитектуре, читљивости или перформанси. Костили су неизбежни у реалном развоју: рокови, некомпатибилност верзија, legacy-код и недокументирано понашање framework-а приморавају програмере на компромисе. Према Martin Fowler (2025), кључна разлика између оправданог костила и техничког дуга је постојање плана за његово уклањање и јасно означавање у коду.
Главно
Костил — је сленг назив за програмско решење које је функционално исправно, али технички неоптимално. Такав код ради, пролази тестове и чак иде у производњу, али његово читање изазива жељу да се све препише из почетка. У енглеском говорном подручју користе се термини workaround, kludge (kluge), hack или quick-and-dirty fix.
Термин потиче из свакодневне метафоре: ако се сломи нога столице, може се привезати траком — столица поново стоји, али решење је привремено и неукусно. У програмирању је исто: баг се поправља хардкодом, тајмаут костилом или заобилажењем кроз недокументирано API. Код се компилира, апликација не пада, али решење се не може назвати квалитетним.
Важна разлика: баг (bug) — када код не ради, костил — када код ради, али је лоше дизајниран. Костил је увек свесан избор програмера: „Знам да је неукусно, али управо сада решава проблем”.
Према процени Stripe (2024), програмери проводе просечно 17 сати недељно радећи са техничким дугом и костилима — скоро половину радног времена. То је директан губитак продуктивности тима.
Први и главни разлог — рок (deadline). Када до издања остаје један дан, а критични баг још није поправљен, тим бира брзо решење уместо исправног. Хардкодирање вредности, искључивање провере, додавање sleep() — класични примери deadline костила. Искусни програмер увек означава таква места са TODO или FIXME.
Други разлог — некомпатибилност API. Спољна библиотека или framework се понаша друкачије него што је описано у документацији. Framework не извози потребну класу, метода је означена као deprecated, а алтернативе нема. Програмер је приморан да користи рефлексију, унутрашње API или обилазак. У Javi то може бити приступ кроз setAccessible(true), у Swift-u — @objc и performSelector.
Трећи разлог — legacy-код. Програмер баштинује пројекат написан пре 5–10 година на старој верзији framework-а. Нема времена и буџета за преписивање целог модула, па се нова функционалност „лепи” на стари код кроз костиле. Постепено слојева постаје толико много да се модул претвара у „big ball of mud”.
Четврти разлог — недостатак тестова. Рефакторисање без тестова је опасно: промена архитектуре може да поквари радећу функционалност. Када нема тестова, програмер преферира да дода костил преко радећег кода него да ризикује стабилношћу. Према Google Testing Blog (2024), тимови без тестова 3 пута чешће користе workaround решења.
Класификација костила помаже тиму да разуме са којим типом техничког дуга има посла и одабере праву стратегију уклањања. Погледајмо главне врсте.
Хардкод — најзаступљенији тип. Уместо конфигурације, ресурса или параметра користи се чврсто постављена вредност у коду. Пример: хардкодирани URL сервера, тајмаут 5 секунди, величина фонта 16pt. Хардкод чини код нескалирабилним и захтева поновну компилацију при свакој промени.
Copy-paste — умножавање дела кода са малим изменама уместо издвајања заједничке логике. Класичан симптом: у пројекту постоје 3 сличне методе које се разликују у једној линији. Copy-paste убрзава писање кода у тренутку задатка, али 10 пута успорава његово одржавање у будућности — исправку треба унети на 3 места уместо једног.
Празни try-catch — catch блок који ништа не ради или само логује грешку без обраде. Овакав костил „гуши” изузетак, али не решава његов узрок. Апликација наставља да ради, али подаци могу бити оштећени, а корисник не добија повратну информацију.
Sleep у коду — Thread.sleep(500) или DispatchQueue.main.asyncAfter за чекање када би требало да буде догађај или повратни позив. Овакав код је непоуздан: на спором уређају 500 ms може да не буде довољно, на брзом пауза ће бити сувишна. Користите CountDownLatch, Semaphore или async/await са правилним тајмингом.
Флагови компатибилности — if-else каскаде који проверавају верзију ОС, модел уређаја или постојање функције. Када флагови постану више од 3–4, код се претвара у шпагети. Решење — Strategy pattern или Feature Flags кроз конфигурацију.
Многи програмери мешају костил и технички дуг. Разлика је у обиму и свесности. Костил — локално, конкретно решење (један метод, једна класа). Технички дуг — системски проблем који утиче на архитектуру модула или целе апликације.
Метафора Ward Cunningham (tvorca termina Technical Debt): технички дуг је као узети кредит у банци. Узимате новац сада да бисте брже саградили кућу, али касније плаћате камату. Костил — као забијати ексер чекићем уместо шрафцигера: посао се обавља, али мање ефикасно.
Један костил не ствара технички дуг. Али 50 костила у једном модулу = архитектурни дуг. Зато је правило тима: сваки костил се биљежи у code review или task tracker-у, а тим редовно (једном у спринту) прегледа накупљене workaround-е.
Према искуству Spotify Engineering (2023), тимови који воде евиденцију костила у коду (кроз специјалну TODO ознаку или custom annotation) скраћују време рефакторисања за 30% — јер не троше сате тражећи проблематична места.
Први корак — инвентаризација. Тражите у бази кода кључне речи: TODO, FIXME, HACK, WORKAROUND, KLUDGE. Савремени IDE их истичу одвојеном бојом. GitHub такође приказује TODO у интерфејсу Pull Request. Направите списак свих костила са приоритетом.
Други корак — приоритизација. Нису сви костили за хитно поправљање. Приоритет = учесталост измена у датотеци × критичност. Ако се датотека мења 2 пута годишње, костил може да сачека. Ако се модул мења у сваком спринту — костил треба поправити први.
Трећи корак — рефакторисање са тестовима. Никада не рефакторишите костил без тестова. Напишите прво тест који проверава тренутно понашање (са костилом), затим рефакторишите, затим уверите се да тест пролази. Без овога, рефакторисање костила може да поквари функционалност због које је написан.
// Пре: hardcodovani URL workaround
fun getApiUrl(): String {
return "https://api.example.com/v2"
}
// После: konfiguracija kroz BuildConfig
fun getApiUrl(): String {
return BuildConfig.API_BASE_URL
}
Четврти корак — аутоматизација. Поставите линтер који забрањује одређене костил обрасце. На примјер, Detekt за Kotlin може да проверава одсуство Thread.sleep() у производном коду, ESLint — да забрањује console.log у пројекту. Ово спречава нове костиле истог типа.
Упркос негативној конотацији термина, костил може бити оправдано решење. Главни услов: костил је привремен, јасно означен и има план замене. У производном коду сваког великог пројекта постоје на стотине оправданих костила.
Ситуација 1: hotfix у производњи. Критични баг пада код свих корисника. Тиму је потребно поправка у року сата. Прави приступ: поправљамо баг било којим начином, постављамо hotfix. Сљутран дан пишемо право решење и затварамо задатак. Hotfix је оправдан костил ако не живи више од 48 сати.
Ситуација 2: чекање изласка нове верзије библиотеке. Framework садржи баг који је поправљен у master-у, али издање за 2 недеље. Уместо писања сложеног обилазног кода, тим додаје workaround са напоменом „REMOVE after library 3.2”. Када изађе 3.2, workaround се уклања.
Ситуација 3: покретање стартапа или MVP. У MVP фази брзина је важнија од архитектуре. Костили на почетку су нормални. Проблем настаје када стартап не прерасте у производ, а костили остају. Препорука: након рунде финансирања одвојите спринт за отплату критичног техничког дуга.
Главни принцип: „Legacy је туђ код без тестова” (Michael Feathers). Ако је костил покривен тестом и јасно документован — управљив је. Ако виси без коментара 2 године у заборављеном модулу — то више није костил, већ архитектурни проблем.
Често постављана питања
Баг — код не ради како што се очекује. Костил — код ради, али је неоптимално написан. Костил је увек свесна одлука програмера, баг — обично несвесна грешка.
Користите // TODO: refactor — ... или прилагођену анотацију @Workaround са пољима: разлог, датум, одговорни, deadline уклањања. Избегавајте // HACK без објашњења.
Ако се модул не мења и костил је стабилан — не треба. Рефакторисање без разлога повећава ризик регресије. Поправљајте само оне костиле који ометају додавање нових функционалности.
Упоредите време: „Сада губимо 4 сата на речно тестирање због ових костила. Рефакторисање ће трајати 8 сати и скратиће време на 30 минута. Повраћај — 2 спринта”. Говорите језиком брзине и новца, а не чисте архитектуре.
Тражите TODO, FIXME, HACK, WORKAROUND путем grep-a кроз пројекат. Анализирајте методе дуже од 100 линија и класе са више од 5 зависности. Користите линтере са прилагођеним правилима за аутоматско откривање.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође