„Приковати“ и „хардкодовати“ су жаргонски термини који значе чврсту фиксацију вредности директно у коду програма, уместо да их извучете у подешавања или конфигурацију. Хардкод је један од најпознатијих анти-образаца у развоју, јер смањује флексибилност и поновну употребљивост кода. Према Refactoring Guru, хардкод отежава тестирање, одржавање и прилагођавање апликације различитим окружењима. Свесно коришћење константи уместо хардкода је знак зреле архитектуре.
Главно
Хардкодовати (приковати) – уградити конкретну вредност у код програма тако да је за њену промену потребно уређивање изворног кода и прекомпајлирање апликације. Метафора „приковати“ тачно одражава суштину: вредност је чврсто фиксирана и може се одвојити од кода само са напором.
Пример хардкода – УРЛ сервера записан као стринг директно у телу функције. Ако сервер пређе на другу адресу, програмер треба да пронађе стринг у коду, промени га, поново изгради апликацију и објави издање. У апликацији са исправном архитектуром такав УРЛ би био извучен у конфигурациони фајл, променљиву окружења или сервис конфигурације.
Термин „приковати“ има емотивнију конотацију: наглашава да је вредност уметнута чврсто и без могућности брзе замене. У српском окружењу оба израза се користе као потпуни синоними са негативном конотацијом. Понекад се хардкод иронично назива „константом извученом у посебну константу из константе“.
Хардкод је анти-образац јер нарушава принципе одржавања, тестирања и проширивости кода. У коду где су вредности „приковане“, свака промена окружења, дизајна или логике захтева ручно тражење и замену у изворима. То повећава ризик од грешака и успорава развој.
Размотримо конкретне последице хардкода на примеру типичне мобилне апликације. Ако је размак свих дугмади задат бројем у коду, а не кроз ресурс – промена дизајна ће захтевати проналажење свих појављивања и замену. Ако је УРЛ крајње тачке чврсто уписан – пребацивање између окружења (dev, stage, prod) није могуће без прекомпајлирања.
| Последица | Опис | Ниво критичности |
|---|---|---|
| Тешкоћа одржавања | Промена захтева претрагу целог кода | Висок |
| Грешке при копирању | Не налазе се и замењују сва појављивања | Висок |
| Немогућност тестирања | Не могу се заменити тест подаци | Средњи |
| Проблеми са локализацијом | Текстови у коду се не преводе | Средњи |
| Отежавање прегледа кода | Прегледач мора да памти све контексте | Низак |
Функција која користи магичне бројеве и чврсто уписане стрингове је класика хардкода. После месец дана аутор неће запамтити шта значе 18, 0.07 и 2.5. После годину дана – нико у тиму неће усудити да мења ове бројеве, плашећи се да поквари логику. Извлачење вредности у именоване константе чини код самодокументујућим.
// Лоше: магични бројеви и стрингови
fun calculatePrice(base: Double): Double {
val tax = base * 0.07
val tip = base * 0.15
val discount = if (base > 100) 10 else 0
return base + tax + tip - discount
}
Хардкодовани УРЛ базе података неће дозволити покретање тестова на локалној in-memory бази података. Програмер ће морати да подигне пуноправни сервер или да исправи код пре тестирања. Извлачење конфигурације из кода решава проблем: тестови користе тест параметре, продукција – продукцијске, а код се притом не мења.
Хардкод је анти-образац, али постоје легитимни изузеци када је чврсто уписана вредност не само дозвољена већ и пожељна. Граница пролази дуж осе променљивости: ако се вредност никада или готово никада не мења у животном циклусу апликације, може се хардкодовати. Ако се макар потенцијално може променити – извуците у конфигурацију.
Математичке и физичке константе – број Пи, убрзање гравитације, број милисекунди у секунди – безбедне су за хардкод. Оне су дефинисане природом или стандардима и неће се променити. Величине константних низова дефинисане спецификацијом такође се могу чврсто фиксирати, али са коментаром о пореклу броја.
Број милисекунди у секунди је стабилна константа дефинисана временским стандардом. Нема смисла извлачити је у конфиг, јер се никада неће променити. Међутим, чак и такве константе је боље декларисати са разумљивим именом, како код не би садржао „магичне бројеве“: уместо 1000 пишите MILLISECONDS_IN_SECOND.
// Оправдан хардкод: стабилне константе
private const val MILLIS_IN_SECOND = 1000
private const val LOGIN_TIMEOUT_SECONDS = 30
fun formatDuration(ms: Long): String {
val seconds = ms / MILLIS_IN_SECOND
return "${seconds} sec."
}
Постоји неколико проверених начина да се избегне хардкод, од којих је сваки погодан за свој тип вредности. Избор алтернативе зависи од тога колико често се вредност мења и ко је мења: програмер, devops или крајњи корисник.
За УРЛ-ове сервера, АПИ кључеве и feature-флагове користите конфигурационе фајлове у форматима JSON, YAML или TOML. На Android-у је то build.gradle са buildConfigField или res/values/config.xml. На iOS-у – Info.plist или xcconfig. Конфигови се компајлирају заједно са апликацијом, али могу бити различити за различите шеме изградње.
За тајне (токени, лозинке) и параметре окружења користите променљиве окружења. Оне не доспевају у репозиторијум и могу се разликовати на dev, stage и prod серверима. У мобилном развоју променљиве окружења се често емулирају кроз Xcode шеме изградње или build flavor-е у Gradle-у.
Стрингови, боје, величине, слике треба да буду извучени у ресурсне фајлове: strings.xml на Android-у, Localizable.strings на iOS-у, ARB фајлове у Flutter-у. То олакшава локализацију, прилагођавање различитим екранима и тамној теми. Промена стринга у ресурсима не захтева преписивање кода.
<!-- Android: res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="api_base_url">https://api.example.com</string>
</resources>
За сервисе и провајдере користите Dependency Injection путем Dagger, Hilt или Koin на Android-у, Swinject на iOS-у. DI оквири омогућавају замену имплементација у лету – за тестове, за различита окружења, за различите кориснике. Ово је највиши ниво апстракције, где се „прикованост“ вредности замењује убризгавањем споља.
Рефакторисање хардкода је процес извлачења чврсто уписаних вредности у конфигурацију или ресурсе. Ово је једна од најсигурнијих операција рефакторисања, ако се изводи методично. Описана секвенца испод је погодна за било који језик и платформу.
Претрага се може обавити кроз IDE (Search in Project) или скриптом. Тражите стрингове, УРЛ-ове, нумеричке литерале, величине, тајмауте. Посебну пажњу обратите на понављајуће вредности: ако се исти број појављује на пет места, то је кандидат за извлачење у константу. Користите grep или уграђену претрагу IDEA / Xcode.
За сваку пронађену вредност креирајте константу са смисленим именом. Групишите константе по модулима или класама. Име треба да објасни шта вредност значи, а не како се користи: API_TIMEOUT, а не TIMEOUT_30. Након замене ниједан број у коду не сме остати без објашњења.
// Пре: магични број 0.4
let cardHeight = screenHeight * 0.4
// После: именована константа
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio
Ако се вредност може мењати између изградњи или окружења – извуците је у конфигурациони фајл или ресурсе апликације. За стрингове користите фајлове локализације. За УРЛ-ове – build config или xcconfig. За величине – ресурсне фајлове (dimens.xml на Android-у). Проверите да се апликација компајлира и ради исправно након извлачења.
Након рефакторисања напишите тест који проверава да се конфигурација учитава исправно и вредности одговарају очекиваним. Ако у будућности неко промени конфиг, тест ће указати на неслагање. Тест конфигурације је брз и поуздан начин да се спречи регресија.
Након извлачења у конфиг проверите да сва места где се користила стара вредност упућују на јединствени извор. Уклоните коментарисани код и старе константе које се више не користе. Финализујте рефакторисање комитом са поруком која описује које вредности и где су извучене.
Често постављана питања
Хардкодовати – чврсто уписати вредност у изворни код уместо извлачења у конфигурацију или ресурсе. То чини код мање флексибилним и сложенијим за одржавање.
Хардкод отежава промену понашања апликације, омета тестирање, ствара дуплирање и повећава ризик од грешака при копирању. Промена хардкодоване вредности захтева прекомпајлирање и поновно објављивање апликације.
Дозвољен за математичке константе, стабилне вредности које се не мењају у животном циклусу апликације и за привремене прототипове. У продукцији чак и константе треба извлачити у именоване променљиве.
Пронађите све магичне бројеве путем претраге, замените их именованим константама или извуците у конфигурациони фајл. Напишите тест који проверава учитавање конфигурације. Уклоните дупликате и направите комит са описом промена.
Константа – именована вредност у коду, доступна за промену на једном месту. Хардкод – неименоване вредности разбацане по коду. Добра пракса: увек користите именоване константе са смисленим називима.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође