Dopo l'emergenza, il debito tecnico: come trasformo un server Linux post-subentro in un asset misurabile nei 90 giorni successivi

Dopo l'emergenza, il debito tecnico: come trasformo un server Linux post-subentro in un asset misurabile nei 90 giorni successivi

Il 4 novembre 2024 ho concluso un subentro di emergenza per una PMI lombarda del settore manifatturiero che gestiva il proprio gestionale interno - preventivi, distinte base, ordini fornitori, bolle di trasporto - su un'applicazione PHP custom da 11 anni di vita, ospitata su un dedicato Hetzner AX41. L'emergenza era stata risolta in tre giorni di lavoro intenso: server compromesso, ricostruito su nuova infrastruttura, dati ripristinati da backup verificato, sito di nuovo in funzione con tutte le integrazioni con fornitori ed ERP esterni. Il cliente, sollevato e contento, mi ha proposto subito di "tornare a fare quello che faccio normalmente" e di chiudere la fattura dell'emergenza. Gli ho detto di no. Gli ho spiegato, davanti a un caffè nel suo ufficio di Brescia, che senza un piano strutturato dei 90 giorni successivi, fra sei mesi avremmo riavuto lo stesso problema in una forma probabilmente peggiore - perché la compromissione aveva fatto emergere sintomi, non aveva risolto le cause.

Ho preso quattro ore di quel pomeriggio per fargli vedere quello che chiamo la "mappa del debito tecnico" del suo sistema: trentuno voci classificate per impatto operativo e sforzo di rimedio, di cui sette critiche, dodici importanti, dodici minori. Alla fine di quelle quattro ore mi ha firmato un retainer di 90 giorni di consolidamento strutturato. Tre mesi dopo le voci critiche erano scese a zero, le importanti a cinque, e per la prima volta in undici anni il sistema aveva backup verificati con restore test mensile, monitoring di base con alert che arrivavano direttamente sul telefono del titolare, e una cronologia Git pulita e leggibile. Questo articolo descrive il piano operativo di quei 90 giorni, calibrato su ciò che ha senso fare in una PMI italiana con budget realistico - non da multinazionale con CISO dedicato - e un team interno che spesso è composto da una sola persona che fa anche altre cose oltre all'IT.

Non è un piano teorico né una traduzione di best practice americane da blog DevOps: è il distillato di sette interventi di consolidamento post-subentro fatti negli ultimi tre anni, con tempi e voci verificabili, e un principio guida che ripeto sempre ai clienti - il debito tecnico è un debito vero, con interessi composti, e ignorarlo nel tempo costa sistematicamente più di pagarlo a rate pianificate.

Cosa significa davvero "misurare" il debito tecnico, e come lo rendo un numero che il cliente capisce?

Il primo passo del piano dei 90 giorni è la misura oggettiva. Senza una misura, "debito tecnico" rimane una sensazione vaga del tipo "il codice fa schifo" oppure "questo sistema è fragile", e non si può pianificare nulla di credibile su una sensazione soggettiva. La definizione operativa che uso nei miei interventi è quella formalizzata originariamente da Ward Cunningham e poi articolata dal Software Engineering Institute della Carnegie Mellon nei loro report sul Technical Debt, raffinata poi da Martin Fowler nel suo Technical Debt Quadrant che distingue fra debito deliberato/involontario e prudente/imprudente: il debito tecnico è la differenza misurabile fra il costo di mantenere il sistema come è ora e il costo di mantenerlo se fosse stato fatto bene fin dall'inizio. È misurabile in ore di lavoro mensili "buttate" per gestire complessità inutile, intervento manuale ripetuto o workaround che potrebbero non esistere.

La mappa che produco nella prima settimana del piano è un foglio di calcolo (o una tabella markdown in un repository interno dedicato al consolidamento) con cinque colonne per ogni voce di debito identificata. Ecco un estratto reale, anonimizzato, della mappa prodotta per il cliente lombardo di novembre 2024:

| # | Voce debito                            | Impatto (h/mese) | Sforzo (h) | Payback (mesi) | Pri |
|---|----------------------------------------|------------------|------------|----------------|-----|
| 1 | Backup non verificati (solo tar cron)  | 4                | 6          | 1.5            | C   |
| 2 | MySQL 5.7 fuori supporto               | 2                | 12         | 6.0            | C   |
| 3 | Cert LE scaduti 2x in 6 mesi           | 3                | 2          | 0.7            | C   |
| 4 | Cron import rotto 1 run su 4           | 8                | 4          | 0.5            | C   |
| 5 | SSH password ancora attivo 2 account   | 1                | 1          | 1.0            | C   |
| 6 | Monitoring assente (scoperto a caso)   | 6                | 8          | 1.3            | C   |
| 7 | Job sincronizzazione con ERP instabile | 5                | 10         | 2.0            | C   |
| 8 | 47 branch Git di cui 32 morti          | 1                | 3          | 3.0            | I   |
| 9 | Nessun test automatico                 | 4                | 20         | 5.0            | I   |

La priorità è ricavata automaticamente dal payback in mesi: Critica se payback < 2 mesi, Importante fra 2 e 4, Minore sopra i 4. Questa classificazione produce automaticamente l'ordine di intervento dei primi 90 giorni: si parte dalle critiche, si fanno tutte, e solo dopo si scende alle importanti. Sul cliente lombardo, tre delle sette voci critiche avevano contribuito direttamente alla compromissione iniziale (SSH password sopravvissuto, monitoring assente che aveva fatto scoprire l'incidente con 36 ore di ritardo, backup non verificati che erano stati ripristinati a fatica). Tutte e sette avevano payback inferiore a 30 giorni di lavoro - il che significa che ripagarle era ovviamente conveniente, era solo mai stato fatto.

I primi 30 giorni: stop the bleeding strutturale

Il primo mese del piano è dedicato esclusivamente alle voci critiche della mappa. Lo chiamo "stop the bleeding strutturale" per distinguerlo dallo stop the bleeding immediato descritto nel protocollo di subentro su server Hetzner/OVH con developer scomparso: durante l'emergenza iniziale fai il minimo per far ripartire il sistema, durante i primi 30 giorni del piano di consolidamento risolvi le condizioni strutturali che hanno reso possibile quell'emergenza. Le voci tipiche che affronto in questo primo mese, e che ripeto con minime variazioni su quasi tutti i clienti: backup verificati con restore test mensile (senza il restore test il backup è un atto di fede, non una procedura di sicurezza); upgrade dei componenti core fuori supporto (PHP, MySQL o MariaDB, distribuzione Linux); hardening minimo di SSH (no password per nessun utente, fail2ban con ban duraturi, accesso solo da bastion WireGuard); monitoring di base con alert reali (Uptime Kuma o equivalente self-hosted, alert via Telegram o email su disco, CPU, RAM, processi critici, certificati); patching automatico delle security update dell'OS con unattended-upgrades configurato.

L'upgrade dei componenti core è quasi sempre la voce più temuta dal cliente perché "se cambia qualcosa il sito non funziona più". La mia esperienza, gestita con il protocollo di refactoring incrementale di codice PHP legacy che ho descritto in un articolo dedicato, è che l'upgrade da PHP 7.x a 8.x è quasi sempre significativamente meno doloroso del previsto: 2-3 giorni di test in staging, una manciata di deprecation warning da risolvere (tipicamente strlen(null) e assegnazioni dinamiche di proprietà), e si è fatto. La differenza è notte e giorno per il rischio di sicurezza: il calendario ufficiale delle versioni supportate è pubblicato su php.net e mostra che PHP 7.4 è fuori supporto da fine 2022, PHP 8.0 da fine 2023, e qualunque infrastruttura ferma a queste versioni nel 2026 è una bomba a tempo che aspetta solo una vulnerabilità zero-day non più patchabile.

Alla fine del primo mese il cliente ha già un sistema sostanzialmente più sicuro e manutenibile. Le metriche concrete che presento nel report di fine mese sono quattro: numero di voci critiche residue sulla mappa (target zero), tempo medio di restore da backup verificato in minuti (target sotto 60), numero di alert generati dal monitoring nelle ultime 4 settimane (serve a stabilire la baseline per i mesi successivi), e versioni dei componenti core con il loro stato di supporto ufficiale. Questo report di fine mese è il documento più importante di tutto il piano, perché è l'evidenza tangibile e misurabile del valore del retainer - il cliente vede cifre, non impressioni.

Dai 30 ai 60 giorni: rimettere il codice sotto controllo

Il secondo mese del piano sposta il focus dal livello sistema al livello codice e affronta le voci importanti della mappa. Le tre attività principali che ripeto quasi sempre sono: pulizia della cronologia Git (commit di "snapshot post-subentro" archiviato in un branch storico, branch obsoleti chiusi, repository riorganizzato con una logica chiara di branching e tagging - perché il versionamento del codice è il prerequisito non negoziabile per la sopravvivenza tecnica di una PMI); introduzione di un primo strato di test automatici sui flussi business-critical (di solito 5-10 test funzionali end-to-end, scritti secondo il principio del harness minimale sui flussi critici che ho descritto per le codebase PHP legacy); creazione di una pipeline CI minimale che esegue questi test ad ogni push, in modo che future regressioni siano intercettate prima del deploy e non scoperte in produzione dal cliente finale.

La pipeline CI che applico tipicamente nel mese 2 è volutamente essenziale. Non servono 14 stage e 5 matrix di Docker: serve qualcosa che gira in meno di 3 minuti, che il team legga ogni giorno e che alerti solo quando c'è davvero qualcosa di rotto. Un esempio concreto di quello che creo come primo ci.yml in un progetto PHP legacy post-consolidamento:

name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, mysql, gd
          coverage: none
      - run: composer install --no-interaction --prefer-dist
      - run: vendor/bin/phpunit --testsuite critical
      - run: vendor/bin/phpstan analyse src --level=2

L'errore operativo che vedo fare più spesso nei clienti in questa fase è il riflesso "facciamo prima un grande refactoring del codice, poi ci mettiamo i test". No. La prima cosa è il test. Senza test, qualunque refactoring successivo è un atto di fede che produce regressioni difficili da diagnosticare dopo settimane. Con anche solo 10 test funzionali che coprono i flussi più importanti del business (login, ciclo ordine, generazione PDF, sincronizzazione con ERP), hai una rete di sicurezza che rende il refactoring successivo verificabile e reversibile. È esattamente l'approccio che Michael Feathers descrive in "Working Effectively with Legacy Code": prima caratterizzi il comportamento esistente con test di caratterizzazione, poi - e solo poi - modifichi il codice.

Sul cliente lombardo del 2024, il secondo mese ha prodotto 12 test funzionali sui flussi di import preventivi, generazione BOM e ordini fornitori, una pipeline CI su GitHub Actions che eseguiva i test ad ogni push su qualunque branch, e una pulizia del repository che ha ridotto il numero di branch attivi da 47 (di cui 32 completamente morti, vecchi di più di un anno) a 6 attivi e chiari. Il debito tecnico misurato a fine secondo mese era passato da 31 voci a 19, con le 7 critiche tutte risolte e 5 delle 12 importanti completate. Il progresso era leggibile direttamente sulla mappa - e presentarla al cliente alla fine del secondo mese ha rinnovato la fiducia sul retainer rimanente.

Dai 60 ai 90 giorni: dalla consolidamento alla manutenzione strutturata

Il terzo mese del piano è il momento in cui si esce dalla modalità "consolidamento di emergenza" e si entra nella modalità "manutenzione strutturata", che è lo stato target a regime. Le tre attività principali sono: definizione e implementazione di una routine di manutenzione mensile (un check-up ripetibile che il cliente o il consulente fa ogni mese e che genera un mini-report standardizzato); definizione di KPI di salute del sistema condivisi col cliente e mostrati su una dashboard leggibile; e - questa è la più importante di tutte - formazione del cliente o del suo team interno sui processi che dovranno gestire da soli dopo la fine del retainer. Il successo di un piano di consolidamento post-subentro non si misura nel mese 3, ma nel mese 12: se a un anno dalla fine del retainer il sistema è ancora sano, il piano ha funzionato. Se è tornato a deteriorarsi, il piano è stato fatto male - e nella mia esperienza la causa è quasi sempre la fase 3 di formazione del cliente saltata o affrettata.

Il framework di KPI che propongo segue lo schema classico delle Four Golden Signals di Site Reliability Engineering descritto da Google nel libro pubblico SRE: latency, traffic, errors, saturation. Per una PMI italiana questi quattro segnali diventano un report mensile semplificato con quattro numeri: uptime mensile in percentuale (target 99.5% minimo), P95 response time in millisecondi sulle pagine principali (target sotto 500ms), numero assoluto di errori 5xx nell'ultimo mese, percentuale di disco, memoria e CPU usate al picco misurato. Sono metriche che chiunque in azienda può leggere senza essere un ingegnere, e che alertano in tempo quando qualcosa sta silenziosamente peggiorando. L'osservabilità che costruisco in questa fase è la stessa descritta nel mio articolo sull'osservabilità minima per applicazioni PHP legacy con logging, metriche e alert, adattata alle dimensioni di un'infrastruttura PMI single-server.

Quanto costa davvero ripagare il debito tecnico, e perché è meno caro di quanto pensi

L'ultima domanda che ogni cliente mi fa è sempre quella sul budget. Il prezzo del piano dei 90 giorni che ho descritto, calibrato su una PMI italiana con un sistema PHP medio-complesso e un singolo server di produzione, è tipicamente nell'ordine di 25-40 ore di consulenza il primo mese, 15-25 il secondo, 10-15 il terzo. Più, una tantum, alcuni costi infrastrutturali eventuali: un nuovo server di staging se non esiste (20-40 euro al mese di cloud), un sistema di monitoring self-hosted, un eventuale upgrade di taglia del server di produzione se è sottodimensionato rispetto ai carichi attuali. Sul cliente standard di una PMI fra 10 e 50 dipendenti, il totale del piano si è storicamente collocato fra 4.000 e 9.000 euro complessivi spalmati sui tre mesi - una cifra gestibile nei budget ordinari di una piccola impresa.

È molto meno di quello che lo stesso cliente avrebbe pagato in incident successivi se non avesse fatto il piano. Sul cliente lombardo del 2024, l'incident di compromissione che aveva preceduto il piano gli era costato circa 17.000 euro fra mancato fatturato dei tre giorni di disservizio, ore di consulenza di emergenza, sostituzione del server dedicato, e una notifica al Garante che ha richiesto l'assistenza legale di uno studio esterno - esattamente lo scenario che ho descritto nel mio protocollo di incident response in 72 ore conforme NIS2 per PMI Laravel e Symfony e che il piano dei 90 giorni serve a non dover mai più ripetere. Sette mesi dopo la fine del retainer di consolidamento, su quel cliente il conteggio degli incident significativi era a zero e le ore di emergenza a zero. Il ritorno dell'investimento sul piano dei 90 giorni, calcolato fra risparmio su incident evitati e riduzione del tempo di intervento manuale ricorrente, è stato di circa il 200% al primo anno e continua a salire man mano che il sistema si stabilizza.

Il principio è quello che ripeto sempre ai clienti PMI che arrivano da un subentro o da un incidente: pagare il debito tecnico è sempre più economico che ignorarlo, ma solo se lo paghi in modo strutturato, con metriche verificabili, con cadenza mensile, con un piano scritto che il cliente approva esplicitamente. Se gestisci una PMI con un sistema PHP critico in produzione che è stato lasciato senza manutenzione strutturata per più di un anno, e ti riconosci nello scenario "il sistema funziona ma ha sempre più piccoli problemi che diventano grandi e imprevedibili", scopri come lavoro con i clienti sul tema del consolidamento post-subentro e del ripagamento strutturato del debito tecnico: in dieci anni di consulenza ho osservato che il fattore decisivo per la sostenibilità di un sistema legacy è quasi sempre la qualità del piano dei primi 90 giorni di consolidamento, non la bravura del singolo developer che ci mette le mani. Se invece sei già nella situazione descritta e vuoi una valutazione operativa del debito tecnico del tuo sistema con un piano d'azione concreto sui 90 giorni successivi, contattami per una consulenza: in due settimane ti consegno la mappa completa del debito tecnico con le sue cinque colonne compilate voce per voce, la classificazione per priorità e payback in mesi, e una proposta di piano di consolidamento calibrata sul budget realistico della tua PMI.

Ultima modifica: