A varázslat a programozásban — nem metafora, hanem pontos kifejezés, amely olyan értékeket (számok, stringek, jelzők) jelöl, amelyek jelentése nem nyilvánvaló a szövegkörnyezetből, és külső ismereteket igényel a megértéshez. A varázslat legelterjedtebb fajtája — magic numbers: közvetlenül a kódba írt numerikus állandók magyarázat nélkül, hogy miért pont ezt az értéket választották. A SonarSource Code Quality Report (2025) kutatás szerint a statikus elemzők összes figyelmeztetésének körülbelül 8 százaléka kapcsolódik megmagyarázatlan literálokhoz. Varázslatos értékek törékennyé teszik a kódot: a változtatás az összes előfordulás megkeresését igényli, és az új fejlesztő nem érti, hogy meg lehet-e érinteni a számot, vagy az kritikus a rendszer működése szempontjából.
Főbb pontok
Varázslat (magic) — minden olyan érték a forráskódban, amelynek jelentése nem nyilvánvaló a tématerület további ismerete nélkül. A kifejezés meghonosodott a közösségben: ha a fejlesztő ránéz egy számra és nem érti, honnan jött — az varázslat.
A varázslat többféle lehet: numerikus (magic numbers), string (magic strings), logikai (magic flags) és konfigurációs (keménykódolt paraméterek, amelyeknek a beállításokban kellene lenniük). Mind a négy típust egy probléma egyesíti: a követelmény változásakor a fejlesztőnek meg kell találnia az összes helyet, ahol az értéket használják, és manuálisan ki kell cserélnie őket. Már egyetlen előfordulás kihagyása is hibához vezet.
A JetBrains Code Quality Survey (2025) jelentése szerint a fejlesztők 73 százaléka a magic numbers-t az alacsony kódminőség jelzőjének tartja, míg 41 százalék elismeri, hogy maga is időnként hagy maga után ilyeneket. A fő ok — sietség: „Később beleteszem az állandót” — de a később soha nem jön el, és egy hónap múlva a 0.85 szám magyarázat nélkül marad a metódus törzsében.
A kulcsszabály: minden literális értéket, kivéve a 0, 1, true, false és az üres stringet, ki kell emelni egy elnevezett állandóba. Kivételek: számláló növelése (i + 1), matematikai nullák (0-ra ellenőrzés) és akkumulátorok kezdeti értékei. Minden más — jelölésre váró jelölt.
Magic number — olyan numerikus literál, amelynek értéke nem nyilvánvaló a szövegkörnyezetből. Klasszikus példa: 86400 a timeout-ért felelős kódban. A fejlesztő látja a számot, és ki kell találnia, hogy ez a másodpercek száma egy napban. Ha téved és 84600-at tesz — a hibát nehéz lesz elkapni, mert a timeout 18 perccel korábban lép működésbe.
Miért veszélyesek a magic numbers: először is rontják az olvashatóságot. Az 1024 szám jelenthet kilobájt méretet, lapozási küszöböt vagy elemek maximális számát. Kontextus nélkül — ez csak egy szám. Másodszor duplikációt hoznak létre: ha az 1024-et öt helyen használják, a küszöb 2048-ra változtatásakor a fejlesztőnek mind az ötöt meg kell találnia és ki kell cserélnie. Ha egy hely kimarad — a rendszer helytelenül működik, de nyilvánvaló hiba nélkül.
// előtte — varázslat tiszta formában
fun calculateTimeout(base: Int): Int {
return base * 3 + 5000
}
// utána — értékek állandókra cserélve
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
}
A harmadik veszély — a tesztelés lehetetlensége. Ha a küszöbérték literálként be van ágyazva a kódba, a teszt nem tudja felülírni a határfeltételek ellenőrzéséhez. A companion object-be vagy konfigurációs fájlba kiemelt állandó tesztelhetővé teszi a kódot: a teszt más értéket ad meg és ellenőrzi a rendszer viselkedését a határon.
Alakítson ki szokást: minden alkalommal, amikor nem 0, 1, 100 vagy 2 számot ír — álljon meg és gondolja át, érdemes-e állandóba kiemelni. Ha a szám üzleti logikához kapcsolódik (korlát, küszöb, timeout, méret) — feltétlenül emelje ki. Ha a szám matematikai állandó (pi, e) — használja a szabványos könyvtárat (Math.PI, Math.E).
Magic strings — string literálok, amelyek állandókba vagy erőforrásokba való kiemelés nélkül vannak beágyazva a kódba. Tipikus példák: végpontok URL-jei, SharedPreferences kulcsok nevei, Intent Actions, bundle keys, fájlnevek és SQL lekérdezések.
A varázslatos stringek veszélye a fordítási szakaszban történő ellenőrzés hiányában rejlik. Egy elírás az „user_prefs” stringben csak futásidőben derül ki. Ha a stringet tíz helyen használják, és a fejlesztő az egyik helyen „user_pref”-t (s nélkül) írt — az alkalmazás nem omlik össze, de az adatok nem kerülnek mentésre. Az ilyen hiba hónapokig élhet az éles rendszerben, mert nem okoz összeomlást.
Android projekteknél a varázslatos stringeket erőforrásokba (strings.xml, arrays.xml) vagy companion object-beli állandókba kell kiemelni. iOS esetén — string erőforrásokba (Localizable.strings) vagy enum állandókba. Backend esetén — konfigurációs fájlokba (.env, application.properties). Egyetlen kulcs, URL vagy útvonal sem szerepelhet a kódban string literálként.
// előtte — varázslatos stringek az osztályban
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")
// utána — stringek enum-ba kiemelve
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)
Fordítson különös figyelmet az ismétlődő stringekre. Ha ugyanaz a „user_settings” kulcs három fájlban fordul elő — 99 százalék valószínűséggel előbb-utóbb az egyikben elírás keletkezik. Enum-ba vagy állandóba való kiemelés garantálja, hogy az összes hivatkozás ugyanazt az értéket használja.
Magic flags — olyan logikai paraméterek, amelyek értéke nem nyilvánvaló a hívás kontextusából. Klasszikus anti-minta: true vagy false átadása egy metódusnak anélkül, hogy megmagyarázná, mit kapcsol be vagy ki pontosan ez a jelző.
Példa: userDao.fetch(includeDeleted = false). A fejlesztő látja a false-t, és nem érti, hogy ez „ne foglalja bele a törölteket” vagy „ne foglalja bele az aktívakat”. Egy hónap múlva a false true-vá változik, és a találatokban megjelennek a törölt rekordok. A hibát csak éles rendszerben fedezik fel.
Megoldás — a logikai jelzők cseréje enum-ra vagy sealed class-ra. A Boolean paraméter helyett használjon UserFilter.includeDeleted vagy UserFilter.activeOnly értéket. Így a kód maga dokumentálja a szándékot, és az IDE felkínálja az elérhető opciókat az automatikus kiegészítés során.
Ha egy logikai jelző több rétegen keresztül van átadva — ez egy újabb jelzés arra, hogy az absztrakció helytelen. Ahelyett, hogy a jelzőt három hívási szinten át vonszolná, gondolja át, hogy a szűrés kiválasztását nem a felső szinten kellene-e meghozni és kész konfigurációként átadni. Minél kevesebb logikai jelző van a kódban — annál kevesebb a varázslat.
Vezessen be egy szabályt: egyetlen logikai paraméter sem kerül átadásra egy metódusnak elnevezett argumentum nélkül (ha a nyelv támogatja a named argumentumokat). Kotlin és Swift esetén ez a követelmény automatikusan teljesül. Java esetén használjon Builder-t vagy enum állandókat a true/false helyett.
A varázslatos értékek keresését statikus elemzők automatizálják, amelyek a literálok váratlan helyeken történő azonosítására vannak konfigurálva. Minden nyelv saját eszközöket kínál konfigurálható kivételekkel.
| Eszköz | Nyelvek | Szabály |
|---|---|---|
| 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 (opt-in engedélyezve) |
| PMD | Java, Apex, PLSQL | MagicNumber (az engedélyezett lista konfigurálható) |
| PhpStorm Inspections | PHP | NumericLiteralWithContext (beépített ellenőrzés) |
A kivételek konfigurálása kritikus fontosságú — enélkül az elemző minden növelésnél (-1, +1) és matematikai nullánál figyelmeztetést ad. SonarQube esetén az engedélyezett számok listája: 0, 1, -1, 2 (duplázáshoz), 100 (százalékok), 60 és 24 (idő). Az összes többi értékhez — elnevezett állandót kell kérni public static final (Java) vagy const val (Kotlin) módosítóval.
A CI szintű elemzéshez adjon hozzá egy lépést a varázslat ellenőrzésével figyelmeztetésként, de ne blokkolja a build-et. Az első futtatás több száz figyelmeztetést mutat az örökölt kódban. Fokozatosan, ticketről ticketre, helyezze át a kódot állandókba és emelje a minőségi küszöböt. Amikor a magic numbers száma 10 alá csökken — kapcsolja be a szabályt build hibaként.
A varázslat refaktorálása — az egyik legbiztonságosabb művelet: a literál állandóra cserélése nem változtatja meg a kód viselkedését. Ennek ellenére a megközelítésnek szisztematikusnak kell lennie, hogy ne maradjanak ki rejtett függőségek (például ha ugyanazt a magic number-t nem kapcsolódó kontextusokban használják, de véletlenül azonos az értékük).
Lépésről lépésre folyamat: keresse meg a varázslatos érték összes előfordulását, értse meg mindegyik kontextusát, ossza szét különböző állandókba (még ha az értékek egyeznek is — a kontextusok eltérőek, és az állandóknak eltérően kell neveződniük), cserélje ki a literálokat állandókra, ellenőrizze tesztekkel. A 2. lépés hibája — a leggyakoribb: két különböző fogalom (timeout milliszekundumban és küszöb bájtokban) numerikusan megegyezhet (például 5000), de szemantikailag ezek különböző mennyiségek, és nem lehet őket egyetlen állandóban egyesíteni.
// előtte — ugyanaz a szám különböző kontextusokban
public class Config {
public void setupCache() {
cache.setMaxSize(5000); // 5 MB
}
public void setupTimeout() {
client.setReadTimeout(5000); // 5 másodperc
}
}
// utána — különböző állandók különböző kontextusokhoz
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
);
}
}
Az új kódhoz a szabály egyszerű: minden literált, kivéve a 0, 1, -1, true, false, null és az üres stringet, ki kell emelni egy állandóba. Kivételek: matematikai állandók (mindig a szabványos könyvtáron keresztül), tesztadatok (a literál maradhat a tesztben, de magyarázó változónévvel) és határértékek növeléshez (i + 1 egy ciklusban — normális).
Gyakran Ismételt Kérdések
Igen, a 100 is magic number, ha kontextus nélkül használják. 100 helyett írja MAX_PERCENT vagy PROBABILITY_SCALE. Kivétel: amikor a 100 nyilvánvaló százalék a kontextusban (például a százalékszámítás képletében), de még ebben az esetben is az állandó javítja az olvashatóságot.
A tesztekben is jobb elnevezett változókat használni. assertEquals(42, result) helyett írja val expected = 42; assertEquals(expected, result). Kivétel: határértékek tesztjei (0, null, üres string) — ezek literálként hagyhatók, mert a teszt kontextusában olvashatóak.
Igen, a felhasználói felülethez kapcsolódó számokat (méretek, margók, animáció időtartama) erőforrásokba kell helyezni (dimens.xml, integers.xml). Az üzleti állandókat (timeout-ok, korlátok) — companion object-be vagy konfigurációs fájlba. A fő kritérium: ha a szám logika megváltoztatása nélkül változhat — az erőforrás.
Futtassa a SonarQube-t a MagicNumber szabállyal vagy az ESLint-et a no-magic-numbers szabállyal. Kapjon jelentést, rendezze használati gyakoriság szerint, és kezdje azokkal a számokkal, amelyek három vagy több helyen fordulnak elő. Ezek a legnagyobb valószínűséggel az állandóba kiemelés jelöltjei.
Nem. Engedélyezett literálok: 0, 1, -1 (növelés/csökkentés, üresség ellenőrzés), true, false, null, üres string. Az összes többi elnevezést igényel. Ha a 0 számot nem üresség ellenőrzésként használják (például 0 — a gyökérkategória ID-ja), akkor a 0-nak is állandónak kell lennie: ROOT_CATEGORY_ID = 0.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is