Magic Numbers sind Zahlen (oder auch Strings), die irgendwo im Code „einfach so“ stehen und deren Bedeutung man nur errät. Du siehst dann 60, 3, 1024 oder "admin" und musst aus dem Kontext heraus rekonstruieren, warum genau dieser Wert dort steht. Zahlen im Code sind nicht per se schlecht. Schlecht wird es, wenn die Zahl eine fachliche oder technische Bedeutung trägt, diese Bedeutung aber nicht sichtbar ist.

Ein typisches Beispiel:

if (timeoutSeconds > 30) {
    throw new IllegalArgumentException("Timeout too high");
}

Die 30 ist lesbar, aber nicht erklärend. 30 Sekunden? Ein Limit aus einer API? Ein Wert, der sich aus Tests ergeben hat? Wenn du später etwas ändern musst, fängt die Suche an. Und sobald du dir nicht sicher bist, wird aus „kleiner Anpassung“ schnell ein „lieber nicht anfassen“.

 

Was an Magic Numbers wirklich nervt

Das Hauptproblem ist Wartbarkeit. Eine Magic Number macht Code schwerer zu lesen, schwerer zu reviewen und schwerer zu ändern. Du erkennst die Absicht nicht sofort, dadurch dauert jedes Lesen länger. Wenn der gleiche Wert an mehreren Stellen vorkommt, riskierst du inkonsistente Änderungen, weil du irgendwann eine Stelle übersiehst. Und du baust unbewusst Kopplungen ein, weil eine Stelle stillschweigend „erwartet“, dass an anderer Stelle genau dieser Wert gilt. Das führt zu Bugs, die nicht offensichtlich sind. Nicht, weil die Zahl falsch ist, sondern weil niemand mehr sicher sagen kann, wofür sie steht.

 

Bessere Alternativen im Java-Alltag

Der Klassiker ist eine benannte Konstante. Sobald ein Wert eine Bedeutung hat, gib ihm einen Namen.

private static final int MAX_TIMEOUT_SECONDS = 30;

if (timeoutSeconds > MAX_TIMEOUT_SECONDS) {
    throw new IllegalArgumentException("Timeout too high");
}

Der Name dokumentiert die Absicht. Und wenn sich das Limit ändert, änderst du es einmal.

Achte darauf, dass der Name die Bedeutung beschreibt, nicht die Zahl. MAX_TIMEOUT_SECONDS hilft dir, THIRTY hilft dir nicht.

Ein zweiter häufiger Fall sind Zahlen-Codes wie 0, 1, 2 für Status oder Typen. Das ist besonders unangenehm, weil du überall Vergleiche gegen Zahlen siehst. Dafür sind enums in Java die sauberste Lösung.

public enum Status { NEW, IN_PROGRESS, DONE }

if (task.getStatus() == Status.DONE) {
    // ...
}

Du bekommst Lesbarkeit und Typsicherheit. „Ungültige“ Werte existieren schlicht nicht.

 

Praktische Leitplanken und typische Stolperstellen

Nicht jede Zahl ist automatisch eine Magic Number. Manche Werte sind so idiomatisch, dass sie ohne extra Namen klar bleiben. 0 als Startwert einer Schleife, 1 fürs Inkrementieren oder -1 als „nicht gefunden“ bei einem Index sind Beispiele, die fast jeder sofort versteht.

Sobald du beim Lesen aber kurz stockst, ist das ein gutes Signal: Gib dem Wert einen Namen oder ergänze eine kurze Erklärung.

Ein weiterer Stolperstein ist die „Constants“-Müllhalde. Eine zentrale Constants-Klasse klingt praktisch, wird aber schnell unübersichtlich. Besser ist es, Konstanten dort zu halten, wo sie fachlich oder technisch hingehören. Ein Timeout-Limit passt zu dem Code, der Timeouts behandelt. Validierungsgrenzen passen zur Validierung. So bleibt der Kontext erhalten.

Wenn du dir unsicher bist, helfen drei Fragen, ohne dass du daraus eine Wissenschaft machst:

  • Versteht man die Bedeutung sofort, ohne dass man raten muss?
  • Wird der Wert an mehreren Stellen gebraucht oder könnte er das bald?
  • Ist der Wert technisch stabil oder fachlich veränderlich?

Wenn du mindestens einmal zögerst, nimm eine Konstante, ein Enum oder Konfiguration.

 

Fazit

Magic Numbers sind selten das eigentliche Problem. Das Problem ist die fehlende Absicht im Code. Ein Name ist günstiger als ein späterer Debugging-Abend. Benannte Konstanten, Enums und sinnvolle Konfiguration sind keine „Nice-to-have“-Politur, sondern reduzieren Risiko bei Änderungen und machen Reviews einfacher. Du schreibst Code nicht (nur) für den Compiler, sondern für Menschen, die ihn lesen und ändern müssen. Und sehr oft bist du das sogar selbst.