Ang mahika sa programming — ay hindi metapora, kundi isang tumpak na terminong tumutukoy sa mga halaga (mga numero, string, flag) na ang kahulugan ay hindi halata sa konteksto at nangangailangan ng panlabas na kaalaman upang maunawaan. Ang pinakalaganap na uri ng mahika — magic numbers: mga numerikong constant na direktang isinulat sa code nang walang paliwanag kung bakit pinili ang partikular na halagang ito. Ayon sa pag-aaral ng SonarSource Code Quality Report (2025), humigit-kumulang 8 porsiyento ng lahat ng babala ng mga static analyzer ay nauugnay sa mga hindi naipaliwanag na literal. Mga mahikang halaga ginagawang marupok ang code: ang pagbabago ay nangangailangan ng paghahanap ng lahat ng paglitaw, at ang bagong developer ay hindi nauunawaan kung maaaring galawin ang numero o ito ay kritikal para sa pagpapatakbo ng sistema.
Mga Pangunahing
Mahika (magic) — ay anumang halaga sa source code na ang kahulugan ay hindi halata nang walang karagdagang kaalaman tungkol sa domain. Ang termino ay naging matatag sa komunidad: kung ang developer ay tumingin sa isang numero at hindi nauunawaan kung saan ito nanggaling — iyon ay mahika.
Ang mahika ay may ilang uri: numerikal (magic numbers), string (magic strings), boolean (magic flags), at konfigurasyonal (hardcode na parameter na dapat nasa settings). Lahat ng apat na uri ay pinag-iisa ng isang problema: kapag nagbago ang requirement, ang developer ay dapat maghanap ng lahat ng lugar kung saan ginagamit ang halaga at palitan ang mga ito nang manu-mano. Ang pagtanggal kahit isang paglitaw ay humahantong sa bug.
Ayon sa ulat ng JetBrains Code Quality Survey (2025), 73 porsiyento ng mga developer ay itinuturing ang magic numbers bilang indikasyon ng mababang kalidad ng code, habang 41 porsiyento ang umamin na sila mismo ay paminsan-minsan ay nag-iiwan ng mga ito. Ang pangunahing dahilan — pagmamadali: “Ilalagay ko ang constant mamaya” — ngunit ang mamaya ay hindi dumarating, at pagkatapos ng isang buwan ang numerong 0.85 ay nananatiling walang paliwanag sa katawan ng metodo.
Ang pangunahing patakaran: bawat literal na halaga, maliban sa 0, 1, true, false, at walang laman na string, ay dapat i-extract sa isang pinangalanang constant. Mga eksepsiyon: pagtaas ng counter (i + 1), matematikong zero (pagsusuri sa 0) at mga panimulang halaga ng mga accumulator. Lahat ng iba pa — kandidato para sa pagpapangalan.
Magic number — ay isang numerikong literal na ang halaga ay hindi halata mula sa konteksto. Klasikong halimbawa: 86400 sa code na responsable para sa timeout. Nakikita ng developer ang numero at dapat hulaan na ito ay bilang ng mga segundo sa isang araw. Kung magkamali siya at maglagay ng 84600 — ang bug ay mahihirapang mahanap dahil ang timeout ay mag-aactivate ng 18 minuto nang mas maaga.
Bakit mapanganib ang magic numbers: una, sinisira nila ang pagiging nababasa. Ang numerong 1024 ay maaaring mangahulugan ng laki ng kilobyte, threshold ng pagination, o maximum na bilang ng mga elemento. Walang konteksto — ito ay numero lamang. Pangalawa, lumilikha sila ng duplikasyon: kung ang 1024 ay ginagamit sa limang lugar, kapag binago ang threshold sa 2048 ang developer ay dapat hanapin lahat ng lima at palitan. Kung may isang lugar na nakaligtaan — ang sistema ay gumagana nang hindi tama, ngunit walang malinaw na error.
// bago — mahika sa dalisay na anyo
fun calculateTimeout(base: Int): Int {
return base * 3 + 5000
}
// pagkatapos — mga halaga pinalitan ng constant
private const val RETRY_MULTIPLIER = 3
private const val BASE_TIMEOUT_MS = 5000
fun calculateTimeout(base: Int): Int {
return base * RETRY_MULTIPLIER + BASE_TIMEOUT_MS
}
Ang ikatlong panganib — kawalan ng kakayahang masubukan. Kung ang threshold na halaga ay naka-embed sa code bilang literal, hindi ito maaaring i-override ng test upang suriin ang mga kondisyon sa hangganan. Ang constant na na-extract sa companion object o configuration file ay ginagawang nasusubok ang code: ang test ay naglalagay ng ibang halaga at sinusuri ang pag-uugali ng sistema sa hangganan.
Magsanay ng ugali: sa bawat oras na magsulat ka ng numerong hindi 0, 1, 100 o 2 — huminto at isipin kung sulit bang i-extract ito sa constant. Kung ang numero ay may kaugnayan sa business logic (limitasyon, threshold, timeout, laki) — i-extract ito nang sapilitan. Kung ang numero ay isang matematikong constant (pi, e) — gamitin ang standard library (Math.PI, Math.E).
Magic strings — mga string literal na naka-embed sa code nang walang pag-extract sa mga constant o resources. Mga tipikal na halimbawa: URL ng mga endpoint, pangalan ng SharedPreferences key, Intent Actions, bundle keys, pangalan ng file, at SQL query.
Ang panganib ng mga mahikang string ay nasa kawalan ng pagsusuri sa yugto ng kompilasyon. Ang typo sa string na “user_prefs” ay hindi matutuklasan hanggang runtime. Kung ang string ay ginagamit sa sampung lugar at ang developer sa isang lugar ay nagsulat ng “user_pref” (walang s) — ang aplikasyon ay hindi crash, ngunit ang data ay hindi nai-save. Ang ganitong bug ay maaaring mabuhay sa produksiyon nang ilang buwan dahil hindi ito nagdudulot ng crash.
Para sa mga proyektong Android, ang mga mahikang string ay dapat i-extract sa mga resources (strings.xml, arrays.xml) o sa mga constant sa companion object. Para sa iOS — sa mga string resources (Localizable.strings) o enum constant. Para sa backend — sa mga configuration file (.env, application.properties). Walang key, URL, o path ang dapat na nasa code bilang string literal.
// bago — mga mahikang string sa buong klase
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")
// pagkatapos — mga string na-extract sa enum
enum PrefKeys: String {
case authToken = "auth_token"
case currentUserId = "current_user_id"
}
prefs.set(token, forKey: PrefKeys.authToken.rawValue)
prefs.set(userId, forKey: PrefKeys.currentUserId.rawValue)
Bigyan ng espesyal na atensyon ang mga string na nauulit. Kung ang parehong key na “user_settings” ay lumitaw sa tatlong file — na may 99 porsiyentong posibilidad na maaga o huli ay magkakaroon ng typo sa isa sa mga ito. Ang pag-extract sa enum o constant ay ginagarantiyahan na ang lahat ng sanggunian ay gumagamit ng parehong halaga.
Magic flags — mga boolean parameter na ang halaga ay hindi halata mula sa konteksto ng tawag. Klasikong anti-pattern: pagpapadala ng true o false sa metodo nang walang paliwanag kung ano ang ina-activate o dea-activate ng flag na ito.
Halimbawa: userDao.fetch(includeDeleted = false). Nakikita ng developer ang false at hindi nauunawaan kung ito ay “huwag isama ang mga tinanggal” o “huwag isama ang mga aktibo”. Pagkalipas ng isang buwan, ang false ay nagiging true at sa mga resulta ay nagsisimulang lumitaw ang mga tinanggal na rekord. Ang bug ay natutuklasan lamang sa produksiyon.
Solusyon — pagpapalit ng mga boolean flag ng enum o sealed class. Sa halip na Boolean parameter, gumamit ng UserFilter.includeDeleted o UserFilter.activeOnly. Sa ganitong paraan, ang code mismo ay nagdodokumento ng intensyon at ang IDE ay nagmumungkahi ng mga available na opsyon sa autocomplete.
Kung ang boolean flag ay dumadaan sa maraming layer — ito ay isa pang senyales na ang abstraksiyon ay mali. Sa halip na kaladkarin ang flag sa tatlong antas ng tawag, isipin kung ang pagpili ng pagsasala ay dapat gawin sa itaas na antas at ipadala bilang handa na configuration. Kung gaano kaunting boolean flag sa code — ganoon kaunting mahika.
Magpatupad ng patakaran: walang boolean parameter na ipinapadala sa metodo nang walang pinangalanang argumento (kung sinusuportahan ng wika ang named arguments). Sa Kotlin at Swift ang kinakailangang ito ay awtomatikong natutupad. Sa Java gamitin ang Builder o enum constant sa halip na true/false.
Ang paghahanap ng mga mahikang halaga ay awtomatiko ng mga static analyzer na naka-configure upang tukuyin ang mga literal sa mga hindi inaasahang lugar. Bawat wika ay nag-aalok ng sarili nitong mga tool na may mga nako-configure na eksepsiyon.
| Tool | Mga Wika | Panuntunan |
|---|---|---|
| SonarQube | Java, Kotlin, Swift, Python, JS | MagicNumber, HardcodedString |
| ESLint | JavaScript, TypeScript | no-magic-numbers, no-hardcoded-strings |
| Detekt | Kotlin | MagicNumber, ComplexCondition |
| SwiftLint | Swift | magic_number (naka-activate na opt-in) |
| PMD | Java, Apex, PLSQL | MagicNumber (maaaring i-configure ang listahan ng pinapayagan) |
| PhpStorm Inspections | PHP | NumericLiteralWithContext (built-in na inspeksyon) |
Ang pagkumpirma ng mga eksepsiyon ay kritikal — kung wala ito, ang analyzer ay magbibigay ng babala sa bawat pagtaas (-1, +1) at matematikong zero. Para sa SonarQube listahan ng pinapayagang numero: 0, 1, -1, 2 (para sa pagdoble), 100 (porsiyento), 60 at 24 (oras). Para sa lahat ng iba pang halaga — humingi ng pinangalanang constant na may modifier na public static final (Java) o const val (Kotlin).
Para sa pagsusuri sa antas ng CI, magdagdag ng hakbang na may pagsusuri ng mahika bilang babala, ngunit hindi humahadlang sa build. Ang unang pagpapatakbo ay magpapakita ng daan-daang babala sa legacy code. Unti-unti, ticket sa ticket, ilipat ang code sa mga constant at itaas ang threshold ng kalidad. Kapag ang bilang ng magic numbers ay naging mas mababa sa 10 — i-activate ang panuntunan bilang error sa build.
Ang refactoring ng mahika — isa sa pinakaligtas na operasyon: ang pagpapalit ng literal sa constant ay hindi nagbabago ng pag-uugali ng code. Gayunpaman, ang diskarte ay dapat sistematiko upang hindi makaligtaan ang mga nakatagong dependency (halimbawa, kung ang parehong magic number ay ginagamit sa mga hindi kaugnay na konteksto ngunit nagkataon na may parehong halaga).
Proseso hakbang-hakbang: hanapin ang lahat ng paglitaw ng mahikang halaga, unawain ang konteksto ng bawat isa, paghiwalayin sa iba't ibang constant (kahit na magkapareho ang mga halaga — magkaiba ang konteksto at ang mga constant ay dapat magkaiba ang pangalan), palitan ang mga literal ng mga constant, suriin sa pamamagitan ng mga test. Ang error sa hakbang 2 — ang pinakakaraniwan: dalawang magkaibang konsepto (timeout sa millisecond at threshold sa byte) ay maaaring magkatugma nang numerikal (halimbawa 5000), ngunit semantikal ang mga ito ay magkaibang dami at hindi maaaring pagsamahin sa isang constant.
// bago — parehong numero sa iba't ibang konteksto
public class Config {
public void setupCache() {
cache.setMaxSize(5000); // 5 MB
}
public void setupTimeout() {
client.setReadTimeout(5000); // 5 segundo
}
}
// pagkatapos — iba't ibang constant para sa iba't ibang konteksto
public class Config {
private static final int CACHE_MAX_SIZE_MB = 5;
private static final int READ_TIMEOUT_SECONDS = 5;
public void setupCache() {
cache.setMaxSize(CACHE_MAX_SIZE_MB * 1024 * 1024);
}
public void setupTimeout() {
client.setReadTimeout(
READ_TIMEOUT_SECONDS * 1000
);
}
}
Para sa bagong code ang patakaran ay simple: bawat literal, maliban sa 0, 1, -1, true, false, null, at walang laman na string, ay ini-extract sa constant. Mga eksepsiyon: matematikong constant (palaging sa pamamagitan ng standard library), test data (maaaring iwan ang literal sa test, ngunit may nagpapaliwanag na pangalan ng variable) at mga hangganan na halaga para sa pagtaas (i + 1 sa loop — normal).
Mga Madalas Itanong
Oo, ang 100 ay magic number din kung ginamit nang walang konteksto. Sa halip na 100, isulat ang MAX_PERCENT o PROBABILITY_SCALE. Eksepsiyon: kapag ang 100 ay halatang porsiyento sa konteksto (halimbawa, sa formula ng pagkalkula ng porsiyento), ngunit kahit sa kasong ito ang constant ay nagpapabuti ng pagiging nababasa.
Sa mga test, mas mainam din na gumamit ng mga pinangalanang variable. Sa halip na assertEquals(42, result) isulat ang val expected = 42; assertEquals(expected, result). Eksepsiyon: mga test sa mga hangganan na halaga (0, null, walang laman na string) — maaaring iwan ang mga ito bilang literal dahil nababasa ang mga ito sa konteksto ng test.
Oo, ang mga numerong may kaugnayan sa UI (sukat, margin, tagal ng animation) ay dapat nasa resources (dimens.xml, integers.xml). Ang mga business constant (timeout, limitasyon) — sa companion object o configuration file. Ang pangunahing pamantayan: kung ang numero ay maaaring magbago nang hindi binabago ang lohika — ito ay isang resource.
Patakbuhin ang SonarQube na may panuntunang MagicNumber o ESLint na may no-magic-numbers. Kumuha ng ulat, ayusin ayon sa dalas ng paggamit at magsimula sa mga numerong lumalabas sa tatlo o higit pa na lugar. Sila ang may pinakamataas na posibilidad na maging kandidato para sa pag-extract sa constant.
Hindi. Pinapayagang literal: 0, 1, -1 (pagtaas/pagbaba, pagsusuri ng kawalan), true, false, null, walang laman na string. Lahat ng iba ay nangangailangan ng pagpapangalan. Kung ang numerong 0 ay ginamit hindi bilang pagsusuri ng kawalan (halimbawa, 0 — ay ID ng root category), kung gayon ang 0 ay dapat ding maging constant: ROOT_CATEGORY_ID = 0.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din