La magia nella programmazione — cos'è, i pericoli dei magic numbers e come sostituirli

Autore: IT Sectr Pubblicato: 2026-07-27 Tempo di lettura: 10 min

La magia nella programmazione non è una metafora, ma un termine preciso che indica valori (numeri, stringhe, flag) il cui significato non è ovvio dal contesto e richiede conoscenze esterne per essere compreso. Il tipo più comune di magia sono i magic numbers: costanti numeriche scritte direttamente nel codice senza spiegazione del perché sia stato scelto proprio quel valore. Secondo il SonarSource Code Quality Report (2025), circa l'8 per cento di tutti gli avvisi degli analizzatori statici sono correlati a letterali non spiegati. I valori magici rendono il codice fragile: modificarli richiede la ricerca di tutte le occorrenze e un nuovo sviluppatore non capisce se il numero può essere toccato o se è critico per il funzionamento del sistema.

Punti chiave

  • Magia — numeri, stringhe e flag impliciti nel codice il cui significato è nascosto al lettore.
  • Magic numbers — letterali numerici senza nome: 86400, 3.14, 0.85, 1024.
  • Stringhe magiche — percorsi, chiavi e URL hardcodati senza estrazione in costanti.
  • Strumenti di rilevamento: SonarQube (regola MagicNumber), ESLint (no-magic-numbers), Detekt.
  • Soluzione: estrarre ogni valore magico in una costante nominata con nome descrittivo.

Cos'è la magia nella programmazione?

Magia è qualsiasi valore nel codice sorgente il cui significato non è ovvio senza conoscenze aggiuntive del dominio. Il termine è consolidato nella comunità: se uno sviluppatore guarda un numero e non sa dire da dove proviene — quella è magia.

La magia si presenta in diversi tipi: numerica (magic numbers), testuale (magic strings), booleana (magic flags) e di configurazione (parametri hardcodati che dovrebbero essere nelle impostazioni). Tutti e quattro i tipi condividono un problema: quando un requisito cambia, lo sviluppatore deve trovare ogni punto in cui il valore viene utilizzato e sostituirli manualmente. Perdere anche una sola occorrenza porta a un bug.

Secondo il JetBrains Code Quality Survey (2025), il 73 per cento degli sviluppatori considera i magic numbers un indicatore di bassa qualità del codice, mentre il 41 per cento ammette di lasciarli occasionalmente. Il motivo principale è la fretta: “Aggiungerò la costante dopo” — ma quel dopo non arriva mai, e un mese dopo il numero 0.85 rimane in mezzo al corpo di un metodo senza spiegazione.

La regola chiave: ogni valore letterale tranne 0, 1, true, false e stringa vuota deve essere estratto in una costante nominata. Eccezioni: incremento del contatore (i + 1), zeri matematici (controllo di 0) e valori iniziali degli accumulatori. Tutto il resto è candidato per essere nominato.

Magic numbers e perché sono pericolosi

Un magic number è un letterale numerico il cui valore non è ovvio dal contesto. Un esempio classico: 86400 nel codice relativo al timeout. Uno sviluppatore vede il numero e deve indovinare che si tratta del numero di secondi in un giorno. Se sbaglia e scrive 84600, il bug sarà difficile da individuare perché il timeout scatterà 18 minuti prima.

Perché i magic numbers sono pericolosi: primo, danneggiano la leggibilità. Il numero 1024 potrebbe significare la dimensione di un kilobyte, una soglia di paginazione o il numero massimo di elementi. Senza contesto — è solo un numero. Secondo, creano duplicazione: se 1024 viene utilizzato in cinque punti, quando la soglia cambia a 2048, lo sviluppatore deve trovarli tutti e cinque e sostituirli. Se un punto viene perso, il sistema funziona in modo errato ma senza un errore esplicito.

Esempio di magic numbers prima e dopo

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
}

Il terzo pericolo è l'impossibilità di testare. Se un valore di soglia è hardcodato come letterale, il test non può sovrascriverlo per verificare le condizioni al contorno. Una costante estratta in un companion object o in un file di configurazione rende il codice testabile: il test sostituisce un valore diverso e verifica il comportamento del sistema al confine.

Sviluppate un'abitudine: ogni volta che scrivete un numero diverso da 0, 1, 100 o 2 — fermatevi e considerate se dovrebbe essere estratto in una costante. Se il numero è correlato alla logica di business (limite, soglia, timeout, dimensione) — estraetelo senza esitazione. Se il numero è una costante matematica (pi, e) — usate la libreria standard (Math.PI, Math.E).

Stringhe e percorsi magici

Le stringhe magiche sono letterali di stringa incorporati nel codice senza essere estratti in costanti o risorse. Esempi tipici: URL degli endpoint, nomi di chiavi SharedPreferences, Intent Action, chiavi bundle, nomi di file e query SQL.

Il pericolo delle stringhe magiche è la mancanza di controllo in fase di compilazione. Un errore di battitura nella stringa “user_prefs” non verrà rilevato fino all'esecuzione. Se la stringa viene utilizzata in dieci punti e lo sviluppatore scrive “user_pref” (senza la s) in uno di essi — l'app non si blocca, ma i dati non vengono salvati. Un tale bug può vivere in produzione per mesi perché non causa un crash.

Per i progetti Android, le stringhe magiche dovrebbero essere estratte in risorse (strings.xml, arrays.xml) o costanti in un companion object. Per iOS — in risorse stringa (Localizable.strings) o costanti enum. Per il backend — in file di configurazione (.env, application.properties). Nessuna chiave, URL o percorso dovrebbe apparire nel codice come letterale di stringa.

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)

Prestate particolare attenzione alle stringhe che vengono duplicate. Se la stessa chiave “user_settings” appare in tre file — al 99 per cento delle volte prima o poi comparirà un errore di battitura in uno di essi. L'estrazione in un enum o costante garantisce che tutti i riferimenti utilizzino lo stesso valore.

Magic flags e parametri booleani

I magic flags sono parametri booleani il cui significato non è ovvio dal contesto della chiamata. Un anti-pattern classico: passare true o false a un metodo senza spiegare cosa quel flag attiva o disattiva esattamente.

Esempio: userDao.fetch(includeDeleted = false). Uno sviluppatore vede false e non sa se significa “non includere eliminati” o “non includere attivi”. Un mese dopo, false si trasforma in true e i record eliminati iniziano ad apparire nell'output. Il bug viene scoperto solo in produzione.

La soluzione è sostituire i flag booleani con un enum o una classe sealed. Invece di un parametro Boolean, utilizzate UserFilter.includeDeleted o UserFilter.activeOnly. In questo modo il codice documenta la sua intenzione e l'IDE suggerisce le opzioni disponibili durante l'autocompletamento.

Se un flag booleano viene passato attraverso più livelli — questo è un altro segnale che l'astrazione è sbagliata. Invece di trascinare un flag attraverso tre livelli di chiamate, considerate se la scelta del filtro dovrebbe essere presa al livello superiore e passata come configurazione pronta. Meno flag booleani ci sono nel codice — meno magia.

Adottate una regola: nessun parametro booleano viene passato a un metodo senza un argomento nominato (se il linguaggio supporta gli argomenti nominati). In Kotlin e Swift, questo requisito è automatico. In Java, utilizzate Builder o costanti enum invece di true/false.

Strumenti di rilevamento della magia

La ricerca di valori magici è automatizzata da analizzatori statici configurati per rilevare letterali in posizioni inaspettate. Ogni linguaggio offre i propri strumenti con eccezioni personalizzabili.

StrumentoLinguaggiRegola
SonarQubeJava, Kotlin, Swift, Python, JSMagicNumber, HardcodedString
ESLintJavaScript, TypeScriptno-magic-numbers, no-hardcoded-strings
DetektKotlinMagicNumber, ComplexCondition
SwiftLintSwiftmagic_number (opt-in)
PMDJava, Apex, PLSQLMagicNumber (elenco consentito configurabile)
Ispezioni PhpStormPHPNumericLiteralWithContext (ispezione incorporata)

Configurare le eccezioni è fondamentale — senza di esse, l'analizzatore segnalerà ogni incremento (-1, +1) e zero matematico. Per SonarQube, l'elenco dei numeri consentiti: 0, 1, -1, 2 (per il raddoppio), 100 (percentuali), 60 e 24 (tempo). Per tutti gli altri valori — richiedete una costante nominata con il modificatore public static final (Java) o const val (Kotlin).

Per l'analisi a livello CI, aggiungete un passo che verifichi la magia come avviso ma non blocchi la build. La prima esecuzione mostrerà centinaia di avvisi nel codice legacy. Gradualmente, ticket dopo ticket, migrate il codice verso costanti e aumentate la soglia di qualità. Quando il numero di magic numbers scende sotto 10 — attivate la regola come errore di build.

Refactoring: sostituire la magia con costanti

Il refactoring della magia è una delle operazioni più sicure: sostituire un letterale con una costante non cambia il comportamento del codice. Tuttavia, l'approccio deve essere sistematico per non perdere dipendenze nascoste (ad esempio, se lo stesso magic number viene utilizzato in contesti non correlati ma ha casualmente lo stesso valore).

Processo passo dopo passo: trovate tutte le occorrenze del valore magico, comprendete il contesto di ciascuna, suddividete in costanti diverse (anche se i valori coincidono — i contesti sono diversi e le costanti dovrebbero avere nomi diversi), sostituite i letterali con costanti, verificate tramite test. L'errore nel passo 2 è il più comune: due concetti diversi (un timeout in millisecondi e una soglia in byte) possono coincidere numericamente (ad esempio, 5000), ma semanticamente sono quantità diverse e non possono essere combinate in un'unica costante.

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
        );
    }
}

Per il nuovo codice, la regola è semplice: qualsiasi letterale tranne 0, 1, -1, true, false, null e stringa vuota viene estratto in una costante. Eccezioni: costanti matematiche (usate sempre la libreria standard), dati di test (i letterali possono rimanere nei test ma con un nome di variabile descrittivo) e valori limite per incremento (i + 1 in un ciclo è ok).

Domande frequenti

100 è un magic number se significa 100 per cento?

Sì, anche 100 è un magic number se usato senza contesto. Invece di 100, scrivete MAX_PERCENT o PROBABILITY_SCALE. Eccezione: quando 100 è ovviamente una percentuale nel contesto (ad esempio, in una formula di calcolo percentuale), ma anche in questo caso una costante migliora la leggibilità.

Che dire dei numeri nei test?

Anche nei test è meglio usare variabili nominate. Invece di assertEquals(42, result), scrivete val expected = 42; assertEquals(expected, result). Eccezione: test per valori limite (0, null, stringa vuota) — possono rimanere come letterali perché sono leggibili nel contesto del test.

I numeri vanno estratti nelle risorse Android?

Sì, i numeri relativi all'interfaccia utente (dimensioni, margini, durata animazioni) dovrebbero essere nelle risorse (dimens.xml, integers.xml). Le costanti di business (timeout, limiti) — in un companion object o file di configurazione. Il criterio principale: se un numero può cambiare senza modificare la logica — è una risorsa.

Come trovare i magic numbers in un progetto legacy?

Eseguite SonarQube con la regola MagicNumber o ESLint con no-magic-numbers. Ottenete il rapporto, ordinate per frequenza di utilizzo e iniziate con i numeri che appaiono in tre o più punti. Sono i candidati più probabili per l'estrazione in costanti.

Ogni numero nel codice va estratto in una costante?

No. Letterali accettabili: 0, 1, -1 (incremento/decremento, controllo vuoto), true, false, null, stringa vuota. Tutti gli altri richiedono denominazione. Se il numero 0 non viene utilizzato come controllo di vuoto (ad esempio, 0 è l'ID della categoria radice), allora anche 0 dovrebbe essere una costante: ROOT_CATEGORY_ID = 0.

Riepilogo

  • Magia — letterali senza spiegazione: numeri, stringhe, flag il cui significato è nascosto al lettore del codice.
  • Magic numbers — costanti numeriche senza nome (86400, 1024, 0.85, 5000) che richiedono conoscenze del dominio per essere comprese.
  • Stringhe magiche — chiavi, URL e percorsi hardcodati invisibili al compilatore che portano a bug in fase di esecuzione.
  • Magic flags — parametri booleani il cui valore non è ovvio (true/false in una chiamata di metodo).
  • Strumenti: SonarQube, ESLint, Detekt, SwiftLint, PMD — tutti supportano la regola MagicNumber.
  • Soluzione: ogni letterale (tranne 0, ±1, true, false, null, "") viene estratto in una costante nominata con nome descrittivo.
  • Contesti diversi — costanti diverse: 5000 come timeout e 5000 come dimensione della cache sono entità diverse.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche