Magie in der Programmierung — was es ist, die Gefahren magischer Zahlen und wie man sie ersetzt

Autor: IT Sectr Veröffentlicht: 2026-07-27 Lesezeit: 10 Min.

Magie in der Programmierung ist keine Metapher, sondern ein präziser Begriff, der Werte (Zahlen, Zeichenfolgen, Flags) bezeichnet, deren Bedeutung aus dem Kontext nicht ersichtlich ist und externes Wissen zum Verständnis erfordert. Die häufigste Art von Magie sind magische Zahlen: numerische Konstanten, die ohne Erklärung, warum dieser bestimmte Wert gewählt wurde, direkt in den Code geschrieben werden. Laut dem SonarSource Code Quality Report (2025) stehen etwa 8 Prozent aller Warnungen statischer Analysewerkzeuge im Zusammenhang mit unerklärten Literalen. Magische Werte machen Code spröde: Änderungen erfordern das Finden aller Vorkommen, und ein neuer Entwickler versteht nicht, ob die Zahl berührt werden kann oder ob sie für die Funktion des Systems kritisch ist.

Wichtige Erkenntnisse

  • Magie — implizite Zahlen, Zeichenfolgen und Flags im Code, deren Bedeutung für den Leser verborgen ist.
  • Magische Zahlen — numerische Literale ohne Namen: 86400, 3.14, 0.85, 1024.
  • Magische Zeichenfolgen — hartcodierte Pfade, Schlüssel, URLs ohne Auslagerung in Konstanten.
  • Erkennungswerkzeuge: SonarQube (Regel MagicNumber), ESLint (no-magic-numbers), Detekt.
  • Lösung: Jeden magischen Wert in eine benannte Konstante mit beschreibendem Namen auslagern.

Was ist Magie in der Programmierung?

Magie ist jeder Wert im Quellcode, dessen Bedeutung ohne zusätzliches Domänenwissen nicht offensichtlich ist. Der Begriff ist in der Gemeinschaft etabliert: Wenn ein Entwickler auf eine Zahl schaut und nicht sagen kann, woher sie stammt — das ist Magie.

Magie tritt in verschiedenen Arten auf: numerisch (magische Zahlen), Zeichenfolgen (magische Zeichenfolgen), boolesch (magische Flags) und Konfiguration (hartcodierte Parameter, die in Einstellungen sein sollten). Allen vier Arten ist ein Problem gemeinsam: Wenn sich eine Anforderung ändert, muss der Entwickler jede Stelle finden, an der der Wert verwendet wird, und sie manuell ersetzen. Das Übersehen auch nur eines Vorkommens führt zu einem Fehler.

Laut der JetBrains Code Quality Survey (2025) betrachten 73 Prozent der Entwickler magische Zahlen als Indikator für niedrige Codequalität, während 41 Prozent zugeben, dass sie sie gelegentlich stehen lassen. Der Hauptgrund ist Eile: „Ich füge die Konstante später hinzu“ — aber dieses später kommt nie, und einen Monat später steht die Zahl 0.85 ohne Erklärung mitten im Methodenrumpf.

Die wichtigste Regel: Jeder Literalwert außer 0, 1, true, false und leerer Zeichenfolge sollte in eine benannte Konstante ausgelagert werden. Ausnahmen: Zählererhöhung (i + 1), mathematische Nullen (Prüfung auf 0) und Anfangswerte von Akkumulatoren. Alles andere ist ein Kandidat für die Benennung.

Magische Zahlen und warum sie gefährlich sind

Eine magische Zahl ist ein numerisches Literal, dessen Wert aus dem Kontext nicht ersichtlich ist. Ein klassisches Beispiel: 86400 in timeout-bezogenem Code. Ein Entwickler sieht die Zahl und muss erraten, dass es sich um die Anzahl der Sekunden eines Tages handelt. Wenn er einen Fehler macht und 84600 schreibt, ist der Fehler schwer zu fangen, weil der Timeout 18 Minuten früher ausgelöst wird.

Warum magische Zahlen gefährlich sind: Erstens beeinträchtigen sie die Lesbarkeit. Die Zahl 1024 könnte eine Kilobyte-Größe, einen Paginierungsschwellwert oder die maximale Anzahl von Elementen bedeuten. Ohne Kontext — ist es nur eine Zahl. Zweitens erzeugen sie Duplikate: Wenn 1024 an fünf Stellen verwendet wird, muss der Entwickler bei einer Änderung des Schwellwerts auf 2048 alle fünf finden und ersetzen. Wird eine Stelle übersehen, arbeitet das System falsch, jedoch ohne expliziten Fehler.

Beispiel für magische Zahlen vorher und nachher

kotlin
// before - magic in its pure form
fun calculateTimeout(base: Int): Int {
    return base * 3 + 5000
}

// after - values replaced with constants
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
}

Die dritte Gefahr ist die Unmöglichkeit des Testens. Wenn ein Schwellwert als Literal hartcodiert ist, kann der Test ihn nicht überschreiben, um Grenzbedingungen zu überprüfen. Eine in ein Companion-Objekt oder eine Konfigurationsdatei ausgelagerte Konstante macht den Code testbar: Der Test setzt einen anderen Wert ein und überprüft das Systemverhalten an der Grenze.

Entwickeln Sie eine Gewohnheit: Jedes Mal, wenn Sie eine Zahl schreiben, die nicht 0, 1, 100 oder 2 ist — halten Sie inne und überlegen Sie, ob sie in eine Konstante ausgelagert werden sollte. Wenn die Zahl mit der Geschäftslogik zusammenhängt (Grenze, Schwellwert, Timeout, Größe) — lagern Sie sie ohne Zögern aus. Wenn die Zahl eine mathematische Konstante (pi, e) ist — verwenden Sie die Standardbibliothek (Math.PI, Math.E).

Magische Zeichenfolgen und Pfade

Magische Zeichenfolgen sind Zeichenfolgenliterale, die in den Code eingebettet sind, ohne in Konstanten oder Ressourcen ausgelagert zu werden. Typische Beispiele: Endpunkt-URLs, SharedPreferences-Schlüsselnamen, Intent-Actions, Bundle-Schlüssel, Dateinamen und SQL-Abfragen.

Die Gefahr magischer Zeichenfolgen ist das Fehlen einer Compile-Zeit-Prüfung. Ein Tippfehler in der Zeichenfolge „user_prefs“ wird erst zur Laufzeit erkannt. Wenn die Zeichenfolge an zehn Stellen verwendet wird und der Entwickler an einer Stelle „user_pref“ (ohne s) schreibt — stürzt die App nicht ab, aber Daten werden nicht gespeichert. Ein solcher Fehler kann monatelang in der Produktion leben, weil er keinen Absturz verursacht.

Für Android-Projekte sollten magische Zeichenfolgen in Ressourcen (strings.xml, arrays.xml) oder Konstanten in einem Companion-Objekt ausgelagert werden. Für iOS — in Zeichenfolgenressourcen (Localizable.strings) oder Enum-Konstanten. Für das Backend — in Konfigurationsdateien (.env, application.properties). Kein Schlüssel, keine URL und kein Pfad sollte im Code als Zeichenfolgenliteral erscheinen.

swift
// before - magic strings across the class
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")

// after - strings extracted to 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)

Achten Sie besonders auf Zeichenfolgen, die dupliziert werden. Wenn derselbe Schlüssel „user_settings“ in drei Dateien vorkommt — wird mit 99-prozentiger Wahrscheinlichkeit irgendwann in einer davon ein Tippfehler auftreten. Das Auslagern in ein Enum oder eine Konstante garantiert, dass alle Referenzen denselben Wert verwenden.

Magische Flags und boolesche Parameter

Magische Flags sind boolesche Parameter, deren Bedeutung aus dem Aufrufkontext nicht ersichtlich ist. Ein klassisches Anti-Pattern: true oder false an eine Methode zu übergeben, ohne zu erklären, was dieses Flag genau aktiviert oder deaktiviert.

Beispiel: userDao.fetch(includeDeleted = false). Ein Entwickler sieht false und kann nicht sagen, ob es „Gelöschte nicht einschließen“ oder „Aktive nicht einschließen“ bedeutet. Einen Monat später wird false zu true, und gelöschte Datensätze erscheinen in der Ausgabe. Der Fehler wird erst in der Produktion entdeckt.

Die Lösung besteht darin, boolesche Flags durch ein Enum oder eine versiegelte Klasse zu ersetzen. Verwenden Sie statt eines Boolean-Parameters UserFilter.includeDeleted oder UserFilter.activeOnly. Auf diese Weise dokumentiert der Code seine Absicht, und die IDE schlägt bei der Autovervollständigung verfügbare Optionen vor.

Wenn ein boolesches Flag durch mehrere Ebenen gereicht wird — ist das ein weiteres Zeichen dafür, dass die Abstraktion falsch ist. Anstatt ein Flag durch drei Aufrufebenen zu schleifen, überlegen Sie, ob die Filterauswahl auf der obersten Ebene getroffen und als fertige Konfiguration übergeben werden sollte. Je weniger boolesche Flags im Code — desto weniger Magie.

Führen Sie eine Regel ein: Kein boolescher Parameter wird ohne benanntes Argument an eine Methode übergeben (wenn die Sprache benannte Argumente unterstützt). In Kotlin und Swift ist diese Anforderung automatisch erfüllt. Verwenden Sie in Java Builder oder Enum-Konstanten anstelle von true/false.

Werkzeuge zur Erkennung von Magie

Die Suche nach magischen Werten wird durch statische Analysewerkzeuge automatisiert, die darauf konfiguriert sind, Literale an unerwarteten Stellen zu erkennen. Jede Sprache bietet ihre eigenen Werkzeuge mit anpassbaren Ausnahmen.

WerkzeugSprachenRegel
SonarQubeJava, Kotlin, Swift, Python, JSMagicNumber, HardcodedString
ESLintJavaScript, TypeScriptno-magic-numbers, no-hardcoded-strings
DetektKotlinMagicNumber, ComplexCondition
SwiftLintSwiftmagic_number (opt-in)
PMDJava, Apex, PLSQLMagicNumber (konfigurierbare erlaubte Liste)
PhpStorm-InspektionenPHPNumericLiteralWithContext (integrierte Inspektion)

Die Konfiguration von Ausnahmen ist entscheidend — ohne sie wird das Analysewerkzeug jede Erhöhung (-1, +1) und jede mathematische Null markieren. Für SonarQube lautet die Liste der erlaubten Zahlen: 0, 1, -1, 2 (für Verdopplung), 100 (Prozentsätze), 60 und 24 (Zeit). Für alle anderen Werte — eine benannte Konstante mit dem Modifikator public static final (Java) oder const val (Kotlin) verlangen.

Für die Analyse auf CI-Ebene fügen Sie einen Schritt hinzu, der Magie als Warnung prüft, aber den Build nicht blockiert. Der erste Durchlauf zeigt Hunderte von Warnungen in Legacy-Code. Migrieren Sie nach und nach, Ticket für Ticket, den Code zu Konstanten und erhöhen Sie die Qualitätsschwelle. Wenn die Anzahl der magischen Zahlen unter 10 fällt — aktivieren Sie die Regel als Build-Fehler.

Refactoring: Magie durch Konstanten ersetzen

Das Refactoring von Magie ist eine der sichersten Operationen: Das Ersetzen eines Literals durch eine Konstante ändert das Codeverhalten nicht. Dennoch muss der Ansatz systematisch sein, um versteckte Abhängigkeiten nicht zu übersehen (z. B. wenn dieselbe magische Zahl in nicht zusammenhängenden Kontexten verwendet wird, aber zufällig denselben Wert hat).

Schritt-für-Schritt-Prozess: Finden Sie alle Vorkommen des magischen Werts, verstehen Sie den Kontext jedes einzelnen, teilen Sie sie in verschiedene Konstanten auf (auch wenn die Werte übereinstimmen — die Kontexte sind unterschiedlich, und die Konstanten sollten unterschiedlich benannt werden), ersetzen Sie die Literale durch Konstanten, überprüfen Sie durch Tests. Der Fehler in Schritt 2 ist der häufigste: Zwei unterschiedliche Konzepte (ein Timeout in Millisekunden und ein Schwellwert in Bytes) können numerisch übereinstimmen (z. B. 5000), aber semantisch handelt es sich um unterschiedliche Größen, die nicht in einer Konstante zusammengefasst werden dürfen.

java
// before - same number in different contexts
public class Config {
    public void setupCache() {
        cache.setMaxSize(5000); // 5 MB
    }
    public void setupTimeout() {
        client.setReadTimeout(5000); // 5 seconds
    }
}

// after - different constants for different contexts
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 neuen Code ist die Regel einfach: Jedes Literal außer 0, 1, -1, true, false, null und leerer Zeichenfolge wird in eine Konstante ausgelagert. Ausnahmen: mathematische Konstanten (immer die Standardbibliothek verwenden), Testdaten (Literale können in Tests bleiben, aber mit einem beschreibenden Variablennamen) und Grenzwerte für Inkremente (i + 1 in einer Schleife ist in Ordnung).

Häufig gestellte Fragen

Ist 100 eine magische Zahl, wenn es 100 Prozent bedeutet?

Ja, 100 ist ebenfalls eine magische Zahl, wenn es ohne Kontext verwendet wird. Schreiben Sie statt 100 MAX_PERCENT oder PROBABILITY_SCALE. Ausnahme: Wenn 100 im Kontext offensichtlich ein Prozentsatz ist (z. B. in einer Prozentberechnungsformel), aber auch in diesem Fall verbessert eine Konstante die Lesbarkeit.

Wie ist es mit Zahlen in Tests?

Auch in Tests ist es besser, benannte Variablen zu verwenden. Schreiben Sie statt assertEquals(42, result) lieber val expected = 42; assertEquals(expected, result). Ausnahme: Tests für Grenzwerte (0, null, leere Zeichenfolge) — sie können als Literale bleiben, weil sie im Testkontext lesbar sind.

Sollten Zahlen in Android-Ressourcen ausgelagert werden?

Ja, Zahlen, die mit der UI zusammenhängen (Größen, Abstände, Animationsdauer), sollten in Ressourcen (dimens.xml, integers.xml) sein. Geschäftskonstanten (Timeouts, Grenzen) — in einem Companion-Objekt oder einer Konfigurationsdatei. Das Hauptkriterium: Wenn eine Zahl geändert werden kann, ohne die Logik zu ändern — ist es eine Ressource.

Wie finde ich magische Zahlen in einem Legacy-Projekt?

Führen Sie SonarQube mit der Regel MagicNumber oder ESLint mit no-magic-numbers aus. Holen Sie sich den Bericht, sortieren Sie nach Nutzungshäufigkeit und beginnen Sie mit Zahlen, die an drei oder mehr Stellen vorkommen. Sie sind die wahrscheinlichsten Kandidaten für die Auslagerung in Konstanten.

Soll wirklich jede Zahl im Code in eine Konstante ausgelagert werden?

Nein. Zulässige Literale: 0, 1, -1 (Inkrement/Dekrement, Leerprüfung), true, false, null, leere Zeichenfolge. Alle anderen erfordern eine Benennung. Wenn die Zahl 0 nicht als Leerprüfung verwendet wird (z. B. ist 0 die ID der Stammkategorie), dann sollte 0 ebenfalls eine Konstante sein: ROOT_CATEGORY_ID = 0.

Zusammenfassung

  • Magie — Literale ohne Erklärung: Zahlen, Zeichenfolgen, Flags, deren Bedeutung für den Leser des Codes verborgen ist.
  • Magische Zahlen — unbenannte numerische Konstanten (86400, 1024, 0.85, 5000), die Domänenwissen zum Verständnis erfordern.
  • Magische Zeichenfolgen — hartcodierte Schlüssel, URLs und Pfade, die für den Compiler unsichtbar sind und zu Laufzeitfehlern führen.
  • Magische Flags — boolesche Parameter, deren Wert nicht offensichtlich ist (true/false in einem Methodenaufruf).
  • Werkzeuge: SonarQube, ESLint, Detekt, SwiftLint, PMD — alle unterstützen die Regel MagicNumber.
  • Lösung: Jedes Literal (außer 0, ±1, true, false, null, "") wird in eine benannte Konstante mit beschreibendem Namen ausgelagert.
  • Unterschiedliche Kontexte — unterschiedliche Konstanten: 5000 als Timeout und 5000 als Cachegröße sind verschiedene Entitäten.

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch