Workaround (anche kludge, hotfix) è una soluzione temporanea o subottimale a un problema nel codice che funziona, ma viola i principi dell'architettura pulita, della leggibilità o delle prestazioni. I workaround sono inevitabili nello sviluppo reale: scadenze, incompatibilità di versioni, codice legacy e comportamento non documentato dei framework costringono gli sviluppatori a fare compromessi. Secondo Martin Fowler (2025), la differenza fondamentale tra un workaround giustificato e il debito tecnico è la presenza di un piano per la sua eliminazione e una marcatura esplicita nel codice.
Punti chiave
Workaround è un termine colloquiale per una soluzione software funzionalmente corretta ma tecnicamente subottimale. Tale codice funziona, supera i test e arriva anche in produzione, ma leggerlo fa venire voglia di riscrivere tutto da capo. Nell'ambiente anglofono si usano i termini workaround, kludge (kluge), hack o quick-and-dirty fix.
Il termine deriva da una metafora domestica: se la gamba di una sedia si rompe, si può fissare con del nastro adesivo — la sedia funziona di nuovo, ma la soluzione è temporanea e brutta. Lo stesso vale nella programmazione: un bug viene corretto con hardcode, un workaround con timeout o un bypass tramite un'API non documentata. Il codice compila, l'applicazione non crasha, ma la soluzione non può essere definita di qualità.
Una differenza importante: un bug — il codice non funziona come previsto. Un workaround — il codice funziona ma è mal progettato. Un workaround è sempre una scelta consapevole dello sviluppatore: “So che è brutto, ma in questo momento risolve il problema.”
Secondo Stripe (2024), gli sviluppatori trascorrono in media 17 ore a settimana a gestire debito tecnico e workaround — quasi la metà del loro tempo lavorativo. Questa è una perdita diretta di produttività del team.
La prima e principale causa sono le scadenze. Quando manca un giorno al rilascio e un bug critico non è ancora stato risolto, il team sceglie una soluzione rapida invece di quella corretta. Hardcodare un valore, disabilitare un controllo, aggiungere sleep() — esempi classici di workaround per scadenze. Uno sviluppatore esperto segna sempre questi punti con TODO o FIXME.
La seconda causa è l'incompatibilità API. Una libreria o framework di terze parti si comporta diversamente da quanto documentato. Il framework non esporta la classe necessaria, un metodo è segnato come deprecato e non c'è alternativa. Lo sviluppatore è costretto a usare reflection, API interne o una soluzione alternativa. In Java, può essere l'accesso tramite setAccessible(true); in Swift — @objc e performSelector.
La terza causa è il codice legacy. Uno sviluppatore eredita un progetto scritto 5-10 anni fa su una versione obsoleta del framework. Non c'è tempo o budget per riscrivere l'intero modulo, quindi la nuova funzionalità viene “incollata” al vecchio codice tramite workaround. Gradualmente si accumulano così tanti strati che il modulo si trasforma in una “grande palla di fango” (big ball of mud).
La quarta causa è la mancanza di test. Il refactoring senza test è pericoloso: modificare l'architettura può rompere funzionalità esistenti. Quando non ci sono test, lo sviluppatore preferisce aggiungere un workaround sul codice funzionante piuttosto che rischiare la stabilità. Secondo Google Testing Blog (2024), i team senza test usano soluzioni workaround 3 volte più spesso.
La classificazione dei workaround aiuta il team a capire che tipo di debito tecnico sta affrontando e a scegliere la giusta strategia di eliminazione. Vediamo i tipi principali.
Hardcode — il tipo più comune. Invece di configurazione, risorsa o parametro, viene usato un valore fisso nel codice. Esempio: un URL del server hardcodato, un timeout di 5 secondi, una dimensione del carattere di 16pt. L'hardcode rende il codice non scalabile e richiede ricompilazione per qualsiasi modifica.
Copia-incolla — duplicare un frammento di codice con piccole modifiche invece di estrarre la logica comune. Sintomo classico: ci sono 3 metodi simili nel progetto che differiscono per una riga. Il copia-incolla accelera la scrittura del codice al momento del compito, ma rallenta 10 volte la manutenzione futura — la correzione deve essere applicata in 3 punti invece di uno.
Try-catch vuoto — un blocco catch che non fa nulla o registra solo l'errore senza gestirlo. Questo workaround “silenzia” l'eccezione ma non ne risolve la causa. L'applicazione continua a funzionare, ma i dati possono essere danneggiati e l'utente potrebbe non ricevere feedback.
Sleep nel codice — Thread.sleep(500) o DispatchQueue.main.asyncAfter per attendere quando dovrebbe esserci un evento o callback. Tale codice non è affidabile: su un dispositivo lento, 500 ms potrebbero non bastare; su uno veloce, la pausa è superflua. Utilizzare CountDownLatch, Semaphore o async/await con tempistiche corrette.
Flag di compatibilità — cascate if-else che controllano la versione del sistema operativo, il modello del dispositivo o la disponibilità di una funzionalità. Quando ci sono più di 3-4 flag, il codice diventa spaghetti. La soluzione è il pattern Strategy o Feature Flags tramite configurazione.
Molti sviluppatori confondono workaround e debito tecnico. La differenza sta nella scala e consapevolezza. Un workaround è una soluzione locale e specifica (un metodo, una classe). Il debito tecnico è un problema sistemico che colpisce l'architettura di un modulo o dell'intera applicazione.
La metafora di Ward Cunningham (creatore del termine Debito Tecnico): il debito tecnico è come prendere un prestito bancario. Prendi soldi ora per costruire la casa più velocemente, ma poi paghi gli interessi. Un workaround — è come piantare un chiodo con un martello invece di una pistola sparachiodi: il lavoro viene fatto, ma meno efficientemente.
Un workaround non crea debito tecnico. Ma 50 workaround in un modulo = debito architetturale. Pertanto, regola del team: ogni workaround viene registrato nella code review o nel task tracker, e il team rivede regolarmente (una volta per sprint) le soluzioni workaround accumulate.
Secondo Spotify Engineering (2023), i team che tracciano i workaround nel codice (tramite un'etichetta TODO speciale o un'annotazione personalizzata) riducono il tempo di refactoring del 30% — perché non perdono ore a cercare i punti problematici.
Il primo passo è l'inventario. Cerca nella codebase le parole chiave: TODO, FIXME, HACK, WORKAROUND, KLUDGE. Gli IDE moderni le evidenziano con un colore separato. GitHub mostra anche TODO nell'interfaccia Pull Request. Crea un elenco di tutti i workaround con priorità.
Il secondo passo è la priorizzazione. Non tutti i workaround devono essere corretti immediatamente. Priorità = frequenza di modifiche nel file × criticità. Se un file cambia 2 volte all'anno, il workaround può aspettare. Se un modulo viene toccato in ogni sprint — il workaround va corretto per primo.
Il terzo passo è il refactoring con test. Non rifattorizzare mai un workaround senza test. Prima scrivi un test che verifichi il comportamento attuale (con il workaround), poi rifattorizza, poi assicurati che il test passi. Senza questo, il refactoring di un workaround può rompere la funzionalità per cui è stato scritto.
// Before: URL workaround hardcodato
fun getApiUrl(): String {
return "https://api.example.com/v2"
}
// After: configurazione tramite BuildConfig
fun getApiUrl(): String {
return BuildConfig.API_BASE_URL
}
Il quarto passo è l'automazione. Configura un linter che proibisca determinati pattern di workaround. Ad esempio, Detekt per Kotlin può verificare l'assenza di Thread.sleep() nel codice di produzione, ESLint può proibire console.log nel progetto. Questo impedisce la comparsa di nuovi workaround dello stesso tipo.
Nonostante la connotazione negativa del termine, un workaround può essere una soluzione giustificata. La condizione principale: il workaround è temporaneo, esplicitamente marcato e ha un piano di sostituzione. Nel codice di produzione di ogni grande progetto ci sono centinaia di workaround giustificati.
Situazione 1: hotfix in produzione. Un bug critico colpisce tutti gli utenti. Il team ha bisogno di una correzione entro un'ora. L'approccio corretto: correggi il bug in qualsiasi modo, distribuisci l'hotfix. Poi, il giorno successivo, scrivi la soluzione appropriata e chiudi il ticket. Un hotfix è un workaround giustificato se non vive più di 48 ore.
Situazione 2: attendere una nuova versione della libreria. Un framework contiene un bug corretto in master, ma il rilascio avverrà tra 2 settimane. Invece di scrivere codice di bypass complesso, il team aggiunge un workaround con la nota “REMOVE after library 3.2.” Quando viene rilasciata la 3.2, il workaround viene rimosso.
Situazione 3: chiusura di una startup o MVP. Nella fase MVP, la velocità è più importante dell'architettura. I workaround all'inizio sono normali. Il problema sorge quando la startup non si trasforma in prodotto, ma i workaround rimangono. Raccomandazione: dopo un round di finanziamento, assegna uno sprint per ripagare il debito tecnico critico.
Il principio principale: “Il codice legacy è codice senza test” (Michael Feathers). Se un workaround è coperto da un test e documentato esplicitamente — è gestibile. Se è rimasto senza commenti per 2 anni in un modulo dimenticato — non è più un workaround, ma un problema architetturale.
Domande frequenti
Bug — il codice non funziona come previsto. Workaround — il codice funziona ma è scritto in modo subottimale. Un workaround è sempre una decisione consapevole dello sviluppatore; un bug è di solito un errore inconsapevole.
Usa // TODO: refactor — ... o un'annotazione personalizzata @Workaround con campi: motivo, data, responsabile, scadenza di rimozione. Evita // HACK nudo senza spiegazione.
Se il modulo non cambia e il workaround è stabile — no. Il refactoring senza motivo aumenta il rischio di regressione. Correggi solo i workaround che impediscono di aggiungere nuove funzionalità.
Confronta il tempo: “Attualmente spendiamo 4 ore in test manuali a causa di questi workaround. Il refactoring richiederà 8 ore e ridurrà il tempo a 30 minuti. Ritorno sull'investimento — 2 sprint.” Parla in termini di velocità e denaro, non di architettura pulita.
Cerca TODO, FIXME, HACK, WORKAROUND tramite grep in tutto il progetto. Analizza metodi più lunghi di 100 righe e classi con più di 5 dipendenze. Usa linter con regole personalizzate per il rilevamento automatico.
Riepilogo
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.
Leggi anche