Magi i programmering — vad är det, varför är magic numbers farliga och ersättning

Författare: IT Sectr Publicerad: 2026-07-27 Lästid: 10 min

Magi i programmering — är inte en metafor, utan en exakt term som betecknar värden (tal, strängar, flaggor) vars innebörd inte är uppenbar från sammanhanget och kräver extern kunskap för att förstå. Den mest utbredda typen av magi — magic numbers: numeriska konstanter inskrivna direkt i koden utan förklaring till varför just detta värde valdes. Enligt undersökningen SonarSource Code Quality Report (2025) är cirka 8 procent av alla varningar från statiska analysatorer relaterade till oförklarade literal. Magiska värden gör kod spröd: ändring kräver sökning efter alla förekomster och en ny utvecklare förstår inte om talet får röras eller om det är kritiskt för systemets funktion.

Huvudpunkter

  • Magi — implicita tal, strängar och flaggor i koden vars innebörd är dold för läsaren.
  • Magic numbers — numeriska literal utan namn: 86400, 3.14, 0.85, 1024.
  • Magiska strängar — hårdkodning av sökvägar, nycklar, URL:er utan extrahering till konstanter.
  • Sökverktyg: SonarQube (regeln MagicNumber), ESLint (no-magic-numbers), Detekt.
  • Lösning: extrahera varje magiskt värde till en namngiven konstant med ett förklarande namn.

Vad är magi i programmering?

Magi (magic) — är varje värde i källkoden vars innebörd inte är uppenbar utan ytterligare kunskap om domänen. Termen har etablerats i communityn: om en utvecklare tittar på ett tal och inte förstår var det kommer ifrån — är det magi.

Magi finns i flera typer: numerisk (magic numbers), sträng (magic strings), boolesk (magic flags) och konfigurationsmässig (hårdkodade parametrar som borde vara i inställningar). Alla fyra typer förenas av ett problem: när ett krav ändras måste utvecklaren hitta alla platser där värdet används och ersätta dem manuellt. Att missa även en förekomst leder till en bugg.

Enligt rapporten JetBrains Code Quality Survey (2025) anser 73 procent av utvecklarna att magic numbers är en indikator på låg kodkvalitet, medan 41 procent erkänner att de själva ibland lämnar kvar dem. Huvudorsaken — brådska: “Jag sätter in konstanten senare” — men senare kommer aldrig, och efter en månad förblir talet 0.85 utan förklaring i metodkroppen.

Nyckelregel: varje literalvärde, förutom 0, 1, true, false och tom sträng, bör extraheras till en namngiven konstant. Undantag: räknarökning (i + 1), matematiska nollor (kontroll på 0) och startvärden för ackumulatorer. Allt annat — kandidat för namngivning.

Magic numbers och varför de är farliga

Magic number — är en numerisk literal vars värde inte är uppenbart från sammanhanget. Klassiskt exempel: 86400 i koden som ansvarar för timeout. Utvecklaren ser talet och måste gissa att det är antalet sekunder på en dag. Om han gör fel och sätter 84600 — kommer buggen att vara svår att hitta eftersom timeouten utlöses 18 minuter tidigare.

Varför magic numbers är farliga: för det första skadar de läsbarheten. Talet 1024 kan betyda storleken på en kilobyte, en pagineringströskel eller det maximala antalet element. Utan sammanhang — är det bara ett tal. För det andra skapar de duplicering: om 1024 används på fem ställen måste utvecklaren vid ändring av tröskeln till 2048 hitta alla fem och ersätta dem. Om ett ställe missas — fungerar systemet felaktigt, men utan uppenbart fel.

Exempel magic numbers före och efter

kotlin
// före — magi i ren form
fun calculateTimeout(base: Int): Int {
    return base * 3 + 5000
}

// efter — värden ersatta med konstanter
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
}

Den tredje faran — omöjlighet att testa. Om tröskelvärdet är inbäddat i koden som en literal kan testet inte åsidosätta det för att kontrollera gränsvillkor. En konstant extraherad till ett companion object eller konfigurationsfil gör koden testbar: testet sätter ett annat värde och kontrollerar systemets beteende vid gränsen.

Utveckla en vana: varje gång du skriver ett tal som inte är 0, 1, 100 eller 2 — stanna och fundera på om det är värt att extrahera det till en konstant. Om talet är relaterat till affärslogik (gräns, tröskel, timeout, storlek) — extrahera det obligatoriskt. Om talet är en matematisk konstant (pi, e) — använd standardbiblioteket (Math.PI, Math.E).

Magiska strängar och sökvägar

Magic strings — strängliteral inbäddade i koden utan extrahering till konstanter eller resurser. Typiska exempel: URL:er till slutpunkter, namn på SharedPreferences-nycklar, Intent Actions, bundle keys, filnamn och SQL-frågor.

Faran med magiska strängar ligger i avsaknaden av kontroll vid kompilering. Ett stavfel i strängen “user_prefs” upptäcks inte förrän vid körning. Om strängen används på tio ställen och utvecklaren på ett ställe har skrivit “user_pref” (utan s) — kraschar inte applikationen, men data sparas inte. En sådan bugg kan leva i produktion i månader eftersom den inte orsakar en krasch.

För Android-projekt måste magiska strängar extraheras till resurser (strings.xml, arrays.xml) eller till konstanter i companion object. För iOS — till strängresurser (Localizable.strings) eller enum-konstanter. För backend — till konfigurationsfiler (.env, application.properties). Ingen nyckel, URL eller sökväg bör finnas i koden som en strängliteral.

swift
// före — magiska strängar i hela klassen
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")

// efter — strängar extraherade till 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)

Ägna särskild uppmärksamhet åt strängar som dupliceras. Om samma nyckel “user_settings” förekommer i tre filer — kommer det med 99 procents sannolikhet förr eller senare att uppstå ett stavfel i en av dem. Extrahering till enum eller konstant garanterar att alla referenser använder samma värde.

Magic flags och booleska parametrar

Magic flags — booleska parametrar vars värde inte är uppenbart från anropets sammanhang. Klassiskt anti-mönster: att skicka true eller false till en metod utan att förklara vad denna flagga exakt aktiverar eller inaktiverar.

Exempel: userDao.fetch(includeDeleted = false). Utvecklaren ser false och förstår inte om detta betyder “inkludera inte borttagna” eller “inkludera inte aktiva”. En månad senare förvandlas false till true och i resultaten börjar borttagna poster dyka upp. Buggen upptäcks först i produktion.

Lösning — ersättning av booleska flaggor med enum eller sealed class. Använd UserFilter.includeDeleted eller UserFilter.activeOnly istället för Boolean-parametern. På så sätt dokumenterar koden själv avsikten och IDE föreslår tillgängliga alternativ vid autokomplettering.

Om en boolesk flagga skickas genom flera lager — är detta ytterligare en signal att abstraktionen är felaktig. Istället för att dra flaggan genom tre anropsnivåer, fundera på om filtervalet borde göras på den övre nivån och skickas som en färdig konfiguration. Ju färre booleska flaggor i koden — desto mindre magi.

Inför en regel: ingen boolesk parameter skickas till en metod utan namngivet argument (om språket stöder named arguments). I Kotlin och Swift uppfylls detta krav automatiskt. I Java använd Builder eller enum-konstanter istället för true/false.

Verktyg för att upptäcka magi

Sökningen efter magiska värden automatiseras av statiska analysatorer som är konfigurerade för att identifiera literal på oväntade platser. Varje språk erbjuder egna verktyg med konfigurerbara undantag.

VerktygSpråkRegel
SonarQubeJava, Kotlin, Swift, Python, JSMagicNumber, HardcodedString
ESLintJavaScript, TypeScriptno-magic-numbers, no-hardcoded-strings
DetektKotlinMagicNumber, ComplexCondition
SwiftLintSwiftmagic_number (aktiverad opt-in)
PMDJava, Apex, PLSQLMagicNumber (lista över tillåtna kan konfigureras)
PhpStorm InspectionsPHPNumericLiteralWithContext (inbyggd inspektion)

Konfiguration av undantag är kritisk — utan den kommer analysatorn att utfärda varningar vid varje ökning (-1, +1) och matematisk nolla. För SonarQube är listan över tillåtna tal: 0, 1, -1, 2 (för dubblering), 100 (procent), 60 och 24 (tid). För alla andra värden — kräv en namngiven konstant med modifieraren public static final (Java) eller const val (Kotlin).

För analys på CI-nivå, lägg till ett steg med kontroll av magi som en varning, men inte blockera bygget. Den första körningen kommer att visa hundratals varningar i äldre kod. Gradvis, ticket för ticket, migrera koden till konstanter och höj kvalitetströskeln. När antalet magic numbers blir mindre än 10 — aktivera regeln som byggfel.

Refaktorisering: ersätter magi med konstanter

Refaktorisering av magi — en av de säkraste operationerna: att ersätta en literal med en konstant ändrar inte kodens beteende. Trots detta måste tillvägagångssättet vara systematiskt för att inte missa dolda beroenden (till exempel om samma magic number används i orelaterade sammanhang men råkar ha samma värde).

Steg-för-steg-process: hitta alla förekomster av det magiska värdet, förstå sammanhanget för varje, dela upp i olika konstanter (även om värdena är samma — sammanhangen är olika och konstanterna måste namnges olika), ersätt literalerna med konstanter, kontrollera genom tester. Felet i steg 2 — det vanligaste: två olika begrepp (timeout i millisekunder och tröskel i byte) kan numeriskt sammanfalla (till exempel 5000), men semantiskt är de olika storheter och kan inte förenas i en konstant.

java
// före — samma tal i olika sammanhang
public class Config {
    public void setupCache() {
        cache.setMaxSize(5000); // 5 MB
    }
    public void setupTimeout() {
        client.setReadTimeout(5000); // 5 sekunder
    }
}

// efter — olika konstanter för olika sammanhang
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
        );
    }
}

För ny kod är regeln enkel: varje literal, förutom 0, 1, -1, true, false, null och tom sträng, extraheras till en konstant. Undantag: matematiska konstanter (alltid via standardbiblioteket), testdata (literal kan lämnas i testet, men med ett förklarande variabelnamn) och gränsvärden för ökning (i + 1 i en loop — normalt).

Vanliga frågor

Är 100 ett magic number om det är 100 procent?

Ja, 100 är också ett magic number om det används utan sammanhang. Skriv MAX_PERCENT eller PROBABILITY_SCALE istället för 100. Undantag: när 100 är en uppenbar procentsats i sammanhanget (till exempel i en procentberäkningsformel), men även i detta fall förbättrar en konstant läsbarheten.

Vad gör man med tal i tester?

I tester är det också bättre att använda namngivna variabler. Skriv val expected = 42; assertEquals(expected, result) istället för assertEquals(42, result). Undantag: tester på gränsvärden (0, null, tom sträng) — de kan lämnas som literal eftersom de är läsbara i testsammanhanget.

Är det värt att extrahera tal till Android-resurser?

Ja, tal relaterade till användargränssnittet (storlekar, marginaler, animeringstid) bör vara i resurser (dimens.xml, integers.xml). Affärskonstanter (timeouter, gränser) — i companion object eller konfigurationsfil. Huvudkriteriet: om talet kan ändras utan att ändra logiken — är det en resurs.

Hur hittar man magic numbers i ett äldre projekt?

Kör SonarQube med regeln MagicNumber eller ESLint med no-magic-numbers. Få en rapport, sortera efter användningsfrekvens och börja med tal som förekommer på tre eller fler ställen. De är med största sannolikhet kandidater för extrahering till konstant.

Måste varje tal i koden extraheras till en konstant?

Nej. Tillåtna literal: 0, 1, -1 (ökning/minskning, tomhetskontroll), true, false, null, tom sträng. Alla andra kräver namngivning. Om talet 0 används inte som tomhetskontroll (till exempel 0 — är ID för rotkategorin), då måste 0 också vara en konstant: ROOT_CATEGORY_ID = 0.

Sammanfattning

  • Magi — literal utan förklaring: tal, strängar, flaggor vars innebörd är dold för kodläsaren.
  • Magic numbers — numeriska konstanter utan namn (86400, 1024, 0.85, 5000) som kräver domänkunskap för att förstå.
  • Magic strings — hårdkodning av nycklar, URL:er och sökvägar, osynliga för kompilatorn och leder till runtime-fel.
  • Magic flags — booleska parametrar vars värde inte är uppenbart (true/false i ett metodanrop).
  • Verktyg: SonarQube, ESLint, Detekt, SwiftLint, PMD — alla stöder regeln MagicNumber.
  • Lösning: varje literal (förutom 0, ±1, true, false, null, “”) extraheras till en namngiven konstant med ett förklarande namn.
  • Olika sammanhang — olika konstanter: 5000 som timeout och 5000 som cachestorlek — är olika entiteter.

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också