> ## Content Index
> Fetch the complete content index at: https://bitstales.eu/llms.txt
> Use this file to discover other available public pages before exploring further.

# Un altro if, grazie: cronaca di una funzione in sette riunioni
- URL: https://bitstales.eu/it/proliferare-degli-if/
- Published: 2025-09-06T10:23:00.000Z
- Updated: 2025-09-06T10:23:00.000Z
- Description: Il business cambia idea, il supermanager laureato in Scienze delle Patatine ha una visione, e prezzoFinale() passa da una riga a un labirinto. Autopsia e terapia della proliferazione degli if.
- Author: Vittorio
- Tags: Software Engineering, Programming, #it

Questa è la storia vera, o almeno verosimile, di una funzione. Si chiamava `prezzoFinale()`, era nata sana e aveva una riga. Poi ha conosciuto il **Dott. Brambilla**, Chief Vision Officer, laurea in Scienze delle Patatine, master in Sinergie Trasversali e una convinzione incrollabile: *«È solo un if, quanto ci vuole?»*.

I personaggi sono inventati. Gli `if`, purtroppo, sono reali: li trovate in qualsiasi repository con più di tre anni di vita. Seguiamo la funzione riunione dopo riunione, poi facciamo l'autopsia e infine vediamo come si poteva salvare.

## Riunione 1: «Facciamo uno sconto del 10%»

Lunedì mattina. Il Dott. Brambilla ha letto un articolo sulla fidelizzazione. Richiesta semplice, codice semplice:

```javascript
function prezzoFinale(ordine) {
  return ordine.totale * 0.9;
}
```

Una riga, un test, nessun dubbio. Godetevela, perché non durerà.

## Riunione 2: «Ma solo per i clienti premium. E non il sabato»

«Il sabato il negozio è già pieno», spiega Brambilla, con lo sguardo di chi ha visto un grafico una volta.

```javascript
function prezzoFinale(ordine) {
  if (ordine.cliente.premium && new Date().getDay() !== 6) {
    return ordine.totale * 0.9;
  }
  return ordine.totale;
}
```

Notate il primo campanello d'allarme: la funzione ora dipende dall'**ora di sistema**. Il test che passava venerdì fallirà sabato. Il fuso orario del server è UTC, quello del negozio no: d'estate, tra mezzanotte e le due del sabato (ora italiana), il server crede che sia ancora venerdì e applica lo sconto. Nessuno se ne accorgerà per otto mesi.

## Riunione 3: «I clienti di Bergamo hanno il 15%, ma solo con bonifico»

C'è stato un accordo commerciale. Nessuno sa con chi, ma c'è stato.

```javascript
  if (ordine.cliente.provincia === "BG" && ordine.pagamento === "bonifico") {
    return ordine.totale * 0.85;
  }
```

Domanda che nessuno ha fatto: un cliente premium di Bergamo, che paga con bonifico, ha il 10% o il 15%? O il 25%? Dipende da quale `if` viene prima nel codice. Quindi la politica commerciale dell'azienda la sta decidendo l'**ordine delle righe**.

## Riunione 4: «Il Black Friday vale doppio, tranne sui prodotti già scontati»

```javascript
  if (isBlackFriday() && !ordine.righe.some(r => r.prodotto.giaScontato)) {
    sconto *= 2;
  }
```

«Già scontato» rispetto a cosa? Al listino, al prezzo di ieri o al prezzo del concorrente? Tre interpretazioni, tre sviluppatori, tre comportamenti diversi in tre punti diversi del codice.

## Riunione 5: «Il cliente Rossi ha un accordo speciale»

```javascript
  if (ordine.cliente.id === 4812) {   // Rossi, accordo 2025 (chiedere a Brambilla)
    return ordine.totale * 0.7;
  }
```

Un identificativo cliente scritto nel codice è un contratto commerciale salvato nel posto sbagliato. Quando Rossi cambierà partita IVA, verrà fuori un account duplicato. E quando Brambilla cambierà azienda, nessuno saprà più cosa significa quel commento.

## Riunione 6: «Togliete lo sconto del sabato. Anzi no, rimettetelo, ma solo a marzo»

A questo punto la funzione ha 60 righe e una nuova variabile globale, `SCONTO_SABATO_ATTIVO`, letta da un file di configurazione che esiste solo in produzione. Il codice del "solo a marzo" sta in un altro modulo, perché «era più veloce così».

## Riunione 7: «Ho visto una demo: vogliamo i prezzi dinamici con l'IA»

Settembre 2025: non c'è riunione aziendale senza la parola "agentico". Brambilla ha visto una presentazione in cui un modello sceglie il prezzo ottimale in tempo reale. La richiesta è di farlo «entro fine sprint, ma senza toccare le regole esistenti».

```javascript
function prezzoFinale(ordine) {
  if (FEATURE_IA_PREZZI) {
    const p = await suggerisciPrezzoIA(ordine);       // async in una funzione sync. Già.
    if (p && p > 0 && p < ordine.totale * 2) return p; // "guardrail"
  }
  if (ordine.cliente.id === 4812) return ordine.totale * 0.7;
  let sconto = 0;
  if (ordine.cliente.premium) {
    if (giornoNegozio() !== 6 || (SCONTO_SABATO_ATTIVO && meseCorrente() === 3)) {
      sconto = 0.10;
    }
  }
  if (ordine.cliente.provincia === "BG") {
    if (ordine.pagamento === "bonifico") {
      if (sconto < 0.15) sconto = 0.15;   // o si sommano? (vedi ticket #1123, chiuso senza risposta)
    }
  }
  if (isBlackFriday() && !ordine.righe.some(r => r.prodotto.giaScontato)) sconto *= 2;
  // TODO: rimuovere dopo la campagna di primavera 2023
  if (CAMPAGNA_PRIMAVERA) sconto += 0.05;
  return ordine.totale * (1 - sconto);
}
```

Fine della cronaca. La funzione è ancora in produzione. Nessuno la tocca. Il file ha un commento in cima: `// NON MODIFICARE SENZA PARLARE CON BRAMBILLA`. Brambilla nel frattempo è stato promosso.

## Autopsia: perché gli if si moltiplicano

**1\. La complessità cresce in modo esponenziale, non lineare.** Nel 1976 Thomas McCabe definisce la *complessità ciclomatica*: il numero di cammini indipendenti in una funzione, che nella pratica è il numero di decisioni più uno. McCabe suggeriva di non superare 10\. La nostra funzione è sopra 15\. Ma il dato che fa paura è un altro: con *n* condizioni indipendenti le combinazioni possibili sono **2n**. Sette condizioni fanno 128 scenari. Quanti ne ha testati qualcuno? E quanti ne ha *definiti* il business?

**2\. Ogni if è una regola di business mascherata.** «Bergamo ha il 15%» non è logica di programma: è una politica commerciale, con un responsabile, una data di inizio e (si spera) una data di fine. Scritta come `if`, perde tutto questo: non ha una scadenza, non ha un autore, non ha una priorità rispetto alle altre regole. Resta lì per sempre.

**3\. Gli if morti non muoiono mai.** Il caso più famoso è **Knight Capital**, il 1° agosto 2012\. Un nuovo deploy riusa un vecchio flag, che anni prima attivava una funzione di trading dismessa chiamata "Power Peg". Su uno degli otto server il nuovo codice non viene installato. Il flag riaccende il codice morto, che inizia a comprare e vendere azioni senza controllo. In 45 minuti l'azienda perde circa 440 milioni di dollari, e pochi mesi dopo non esiste più come società indipendente. Il `// TODO: rimuovere dopo la campagna di primavera 2023` della nostra funzione è parente stretto di quel flag.

**4\. Il codice rispecchia l'organigramma.** La *legge di Conway* (1967) dice che i sistemi copiano la struttura di comunicazione delle organizzazioni che li costruiscono. In `prezzoFinale()` si vede bene: ogni ramo è una riunione, ogni eccezione è un dirigente, ogni commento «chiedere a» è una persona che nel frattempo ha cambiato lavoro.

**5\. Il business non sa cosa vuole, e va bene così.** Questa è la parte meno popolare tra i programmatori, ma è vera. Fred Brooks lo scrisse nel 1986 in *No Silver Bullet*: *«The hardest single part of building a software system is deciding precisely what to build.»* Che i requisiti cambino non è un difetto del business: è il modo in cui si scopre cosa serve. Il difetto è trasformare ogni idea esplorativa in un `if` permanente, senza modello, senza scadenza e senza che nessuno decida cosa succede quando due regole si sovrappongono.

## Terapia: come si salvava prezzoFinale()

**Appiattite con le guard clause.** Il primo intervento è estetico ma importante: uscire presto invece di annidare. La "freccia" di `if` dentro `if` diventa una lista leggibile.

**Trasformate le regole in dati.** Le regole commerciali hanno attributi: condizione, sconto, priorità, validità, responsabile. Mettetele in una struttura dati (o in una tabella) e scrivete *un solo* motore che le applica:

```javascript
const REGOLE = [
  { id: "accordo-rossi",  priorita: 100, quando: o => o.cliente.contratto === "ROSSI-2025",
    sconto: 0.30, cumulabile: false, valida: ["2025-01-01", "2025-12-31"], owner: "commerciale" },
  { id: "bergamo-bonifico", priorita: 50, quando: o => o.cliente.provincia === "BG" && o.pagamento === "bonifico",
    sconto: 0.15, cumulabile: false, valida: ["2025-03-01", "2025-12-31"], owner: "vendite nord" },
  { id: "premium",        priorita: 10, quando: (o, ctx) => o.cliente.premium && ctx.giornoNegozio !== 6,
    sconto: 0.10, cumulabile: true,  valida: ["2025-01-01", null], owner: "marketing" },
];

function prezzoFinale(ordine, ctx) {          // ctx: data e ora del negozio, passate dall'esterno
  const attive = REGOLE
    .filter(r => inValidita(r.valida, ctx.data) && r.quando(ordine, ctx))
    .sort((a, b) => b.priorita - a.priorita);
  const prima = attive[0];
  const sconto = !prima ? 0
    : prima.cumulabile ? attive.filter(r => r.cumulabile).reduce((s, r) => s + r.sconto, 0)
    : prima.sconto;
  return ordine.totale * (1 - Math.min(sconto, 0.5));   // tetto esplicito
}
```

Non è magia. Però «solo a marzo» diventa una data invece di un `if`, «chi vince tra Bergamo e premium» diventa un numero che il business deve *scegliere*, e la data e l'ora arrivano dall'esterno, quindi i test non dipendono più dal giorno in cui girano. In più ogni regola ha un responsabile a cui fare domande.

**Usate il polimorfismo quando cambia il *modello*, non il numero.** Se i clienti business hanno un listino diverso, le aste hanno un altro algoritmo e gli abbonamenti si fatturano a parte, quelli non sono tre `if`: sono tre strategie di prezzo (vedi la scheda sui design pattern). È il refactoring che Martin Fowler chiama *Replace Conditional with Polymorphism*.

**Date ai feature flag un responsabile e una scadenza.** Ogni flag nasce con una data di rimozione e un ticket. Un controllo nella CI che fallisce quando un flag è scaduto vale più di cento promemoria. Knight Capital insegna che un flag riciclato è un'arma carica.

**Fatevi dare esempi, non frasi.** «Sconto ai premium tranne il sabato» è una frase. Una tabella di esempi concordata con il business (*Specification by Example*, Gojko Adzic, 2011) è una specifica, e diventa subito un test:

| Cliente | Provincia | Pagamento | Giorno                        | Sconto atteso |
| ------- | --------- | --------- | ----------------------------- | ------------- |
| premium | MI        | carta     | martedì                       | 10%           |
| premium | BG        | bonifico  | martedì                       | **?**         |
| premium | BG        | bonifico  | sabato di marzo, Black Friday | **??**        |

Quei punti interrogativi sono il risultato più prezioso del lavoro di un ingegnere: sono decisioni di business che *nessuno ha ancora preso*. Senza la tabella, le avrebbe prese l'ordine delle righe di codice.

**Diffidate del "motore di regole universale".** L'altro estremo esiste ed è altrettanto pericoloso. Quando le regole diventano un linguaggio di programmazione fatto in casa, in XML o YAML, avete costruito un secondo linguaggio senza debugger, senza test e senza tipi. Si chiama *inner-platform effect*. Gli `if` non sono spariti, si sono solo spostati in un file dove nessuno li vede.

## E il programmatore? Non solo vittima

Sarebbe comodo dare tutta la colpa al Dott. Brambilla, ed è anche divertente. Ma ogni `if` scritto senza fare domande è un `if` che abbiamo firmato noi. Il lavoro dell'ingegnere non è tradurre frasi in condizioni: è trasformare desideri vaghi in **modelli** con regole esplicite, priorità e scadenze. A volte, la cosa più utile da dire in una riunione è anche la più semplice:

> «Va bene. E se un cliente premium di Bergamo paga con bonifico un sabato di marzo durante il Black Friday, quanto paga?»

Il silenzio che segue vale più di qualsiasi refactoring. È lì che il business scopre cosa vuole davvero. E dato che il silenzio di Brambilla di solito dura fino al prossimo cambio di strategia, conviene che la risposta finisca in una tabella e non in un `if`.

Alla prossima storia di bit. `else` niente. 🧾