„Bătut în cuie” și „hardcodat” sunt termeni argotici care înseamnă fixarea rigidă a valorilor direct în codul programului, în loc să le externalizezi în setări sau configurație. Hardcodul este unul dintre cele mai cunoscute anti-pattern-uri în dezvoltare, deoarece reduce flexibilitatea și reutilizabilitatea codului. Potrivit Refactoring Guru, hardcodul complică testarea, întreținerea și adaptarea aplicației la diferite medii. Utilizarea conștientă a constantelor în loc de hardcod este un semn al arhitecturii mature.
Principalele
A hardcoda (a băte în cuie) – a încorpora o valoare specifică în codul programului astfel încât pentru a o modifica este necesară editarea codului sursă și recompilarea aplicației. Metafora „bătut în cuie” reflectă exact esența: valoarea este fixată definitiv și poate fi separată de cod doar cu efort.
Exemplu de hardcod – URL-ul serverului scris ca string direct în corpul funcției. Dacă serverul se mută la o altă adresă, dezvoltatorul trebuie să găsească stringul în cod, să îl modifice, să recompileze aplicația și să lanseze o nouă versiune. Într-o aplicație cu arhitectură corectă, un astfel de URL ar fi externalizat într-un fișier de configurare, o variabilă de mediu sau un serviciu de configurare.
Termenul „bătut în cuie” are o conotație mai emoțională: subliniază că valoarea este fixată rigid fără posibilitatea de înlocuire rapidă. În mediul românesc, ambele expresii sunt folosite ca sinonime complete cu conotație negativă. Uneori hardcodul este numit ironic „constantă externalizată într-o constantă separată dintr-o constantă”.
Hardcodul este un anti-pattern deoarece încalcă principiile de întreținere, testare și extensibilitate ale codului. În codul în care valorile sunt „bătute în cuie”, orice modificare a mediului, designului sau logicii necesită căutare manuală și înlocuire în surse. Acest lucru crește riscul de erori și încetinește dezvoltarea.
Să analizăm consecințele concrete ale hardcodului pe exemplul unei aplicații mobile tipice. Dacă dimensiunea spațierii tuturor butoanelor este dată de un număr în cod, nu printr-o resursă – modificarea designului va necesita găsirea tuturor aparițiilor și înlocuirea lor. Dacă URL-ul endpoint-ului este scris rigid – comutarea între medii (dev, stage, prod) este imposibilă fără recompilare.
| Consecință | Descriere | Nivel de criticitate |
|---|---|---|
| Dificultate de întreținere | Modificarea necesită căutare în tot codul | Ridicat |
| Erori la copiere | Nu toate aparițiile sunt găsite și înlocuite | Ridicat |
| Imposibilitate de testare | Nu se pot substitui date de test | Mediu |
| Probleme de localizare | Textele din cod nu se traduc | Mediu |
| Complexificarea code-review | Reviewerul trebuie să rețină toate contextele | Scăzut |
O funcție care folosește numere magice și stringuri scrise rigid este un exemplu clasic de hardcod. După o lună, autorul nu își va aminti ce înseamnă 18, 0.07 și 2.5. După un an – nimeni din echipă nu va îndrăzni să modifice aceste numere, de teamă să nu strice logica. Externalizarea valorilor în constante denumite face codul autodocumentat.
// Rău: numere magice și stringuri
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
}
URL-ul hardcodat al bazei de date nu va permite rularea testelor pe o bază de date locală in-memory. Dezvoltatorul va trebui să pornească un server complet sau să modifice codul înainte de testare. Externalizarea configurației din cod rezolvă problema: testele folosesc parametri de test, producția – parametri reali, iar codul nu se modifică.
Hardcodul este un anti-pattern, dar există excepții legitime când o valoare scrisă rigid este nu doar admisibilă, ci și preferabilă. Granița trece pe axa variabilității: dacă valoarea nu se schimbă niciodată sau aproape niciodată în ciclul de viață al aplicației, poate fi hardcodată. Dacă cel puțin potențial se poate schimba – externalizează în configurare.
Constantele matematice și fizice – numărul Pi, accelerația gravitațională, numărul de milisecunde într-o secundă – sunt sigure pentru hardcod. Sunt definite de natură sau standarde și nu se vor modifica. Dimensiunile array-urilor constante definite de specificație pot fi, de asemenea, fixate rigid, dar cu un comentariu despre originea numărului.
Numărul de milisecunde într-o secundă este o constantă stabilă definită de standardul de timp. Nu are sens să o externalizezi în config, deoarece nu se va schimba niciodată. Cu toate acestea, chiar și astfel de constante este mai bine să fie declarate cu un nume sugestiv, astfel încât codul să nu conțină „numere magice”: în loc de 1000, scrie MILLISECONDS_IN_SECOND.
// Hardcod justificat: constante stabile
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."
}
Există mai multe metode dovedite pentru a evita hardcodul, fiecare potrivită pentru un tip specific de valori. Alegerea alternativei depinde de cât de des se schimbă valoarea și cine o modifică: dezvoltatorul, devops-ul sau utilizatorul final.
Pentru URL-uri de servere, chei API și feature flags, folosește fișiere de configurare în formate JSON, YAML sau TOML. Pe Android este build.gradle cu buildConfigField sau res/values/config.xml. Pe iOS – Info.plist sau xcconfig. Configurile sunt compilate împreună cu aplicația, dar pot fi diferite pentru diferite scheme de build.
Pentru secrete (tokenuri, parole) și parametri de mediu, folosește variabile de mediu. Acestea nu ajung în repository și pot diferi pe serverele dev, stage și prod. În dezvoltarea mobilă, variabilele de mediu sunt adesea emulate prin scheme de build Xcode sau build flavors în Gradle.
Stringurile, culorile, dimensiunile, imaginile trebuie externalizate în fișiere de resurse: strings.xml pe Android, Localizable.strings pe iOS, fișiere ARB în Flutter. Acest lucru simplifică localizarea, adaptarea la diferite ecrane și tema întunecată. Modificarea unui string în resurse nu necesită rescrierea codului.
<!-- Android: res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="api_base_url">https://api.example.com</string>
</resources>
Pentru servicii și furnizori, folosește Dependency Injection prin Dagger, Hilt sau Koin pe Android, Swinject pe iOS. Framework-urile DI permit înlocuirea implementărilor din zbor – pentru teste, pentru diferite medii, pentru diferiți utilizatori. Acesta este cel mai înalt nivel de abstractizare, unde „fixarea” valorii este înlocuită cu injecția din exterior.
Refactorizarea hardcodului este procesul de externalizare a valorilor scrise rigid în configurare sau resurse. Este una dintre cele mai sigure operații de refactorizare, dacă este executată metodic. Secvența descrisă mai jos este potrivită pentru orice limbaj și platformă.
Căutarea poate fi efectuată prin IDE (Search in Project) sau printr-un script. Caută stringuri, URL-uri, literali numerici, dimensiuni, timeout-uri. Atenție specială la valorile repetitive: dacă același număr apare în cinci locuri, este un candidat pentru externalizare într-o constantă. Folosește grep sau căutarea încorporată IDEA / Xcode.
Pentru fiecare valoare găsită, creează o constantă cu un nume sugestiv. Grupează constantele pe module sau clase. Numele trebuie să explice ce înseamnă valoarea, nu cum este utilizată: API_TIMEOUT, nu TIMEOUT_30. După înlocuire, niciun număr din cod nu trebuie să rămână fără explicație.
// Înainte: numărul magic 0.4
let cardHeight = screenHeight * 0.4
// După: constantă denumită
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio
Dacă valoarea se poate modifica între compilări sau medii – externalizeaz-o într-un fișier de configurare sau în resursele aplicației. Pentru stringuri, folosește fișiere de localizare. Pentru URL-uri – build config sau xcconfig. Pentru dimensiuni – fișiere de resurse (dimens.xml pe Android). Verifică că aplicația se compilează și funcționează corect după externalizare.
După refactorizare, scrie un test care verifică că configurația se încarcă corect și valorile corespund celor așteptate. Dacă în viitor cineva modifică configul, testul va indica neconcordanța. Testul de configurare este o modalitate rapidă și fiabilă de a preveni regresia.
După externalizarea în config, verifică că toate locurile în care se folosea valoarea veche se referă la o sursă unică. Elimină codul comentat și constantele vechi care nu mai sunt utilizate. Finalizează refactorizarea cu un commit având un mesaj care descrie ce valori și unde au fost externalizate.
Întrebări frecvente
A hardcoda – a scrie rigid o valoare în codul sursă în loc să o externalizezi în configurare sau resurse. Acest lucru face codul mai puțin flexibil și mai dificil de întreținut.
Hardcodul complică modificarea comportamentului aplicației, împiedică testarea, creează duplicare și crește riscul de erori la copiere. Modificarea unei valori hardcodate necesită recompilarea și relansarea aplicației.
Admisibil pentru constante matematice, valori stabile care nu se modifică în ciclul de viață al aplicației și pentru prototipuri temporare. În producție, chiar și constantele ar trebui externalizate în variabile denumite.
Găsește toate numerele magice prin căutare, înlocuiește-le cu constante denumite sau externalizează-le într-un fișier de configurare. Scrie un test care verifică încărcarea configurației. Elimină duplicatele și fă un commit cu descrierea modificărilor.
Constanta – o valoare denumită în cod, accesibilă pentru modificare într-un singur loc. Hardcodul – valori nedenumite împrăștiate în tot codul. Bună practică: folosește întotdeauna constante denumite cu nume sugestive.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și