Quattro principi contro il caos algoritmico: perché la disciplina batte l’intelligenza artificiale
La retorica dominante sull’intelligenza artificiale racconta una storia seducente e pericolosa allo stesso tempo, una narrazione in cui modelli linguistici sempre più sofisticati sembrano in grado di sostituire il pensiero ingegneristico con una sorta di intuizione statistica su larga scala, quasi una magia computazionale travestita da competenza. Il problema è che la magia, nei sistemi complessi, tende a rompersi nei momenti peggiori. Quattro principi operativi emergono allora non come linee guida opzionali, ma come una forma di igiene mentale necessaria per evitare che l’output generato diventi una sofisticata forma di errore plausibile, elegante ma sbagliato. Chi ha costruito sistemi reali sa che la differenza tra software funzionante e software credibile ma fallimentare non è nell’intelligenza, ma nella disciplina.
Pensare prima di scrivere codice è un atto quasi sovversivo nel contesto attuale. Il modello medio non pensa, converge. Riempie i vuoti con probabilità, non con comprensione. Questo porta a una dinamica subdola: l’ambiguità non viene esplicitata, viene risolta arbitrariamente. Il risultato è un’illusione di chiarezza. Un sistema che sceglie silenziosamente un’interpretazione tra molte possibili non sta dimostrando capacità, sta semplicemente evitando il conflitto cognitivo. Un ingegnere esperto fa l’opposto. Espone le assunzioni, rende visibili le alternative, introduce frizione dove serve. È un comportamento che rallenta nel breve periodo, ma evita disastri nel lungo. In un mondo ossessionato dalla velocità, dichiarare “non è chiaro” diventa un atto di leadership tecnica. La chiarezza nasce dalla tensione, non dalla sua rimozione.
Una declinazione concreta di questo principio appare quando si chiede a un modello di implementare una funzione apparentemente banale. Un prompt ingenuo genera codice immediato, spesso corretto nel caso medio ma fragile nei bordi. Un approccio disciplinato invece introduce una pausa, un micro-checkpoint cognitivo, che esplicita il contesto prima dell’azione.
# ❌ Approccio implicito (tipico LLM)
def calculate_discount(price, discount):
return price - (price * discount)
# ✅ Approccio disciplinato
# Assunzioni:
# - discount è una percentuale tra 0 e 1?
# - price può essere negativo?
# - servono arrotondamenti?
def calculate_discount(price: float, discount: float) -> float:
if not 0 <= discount <= 1:
raise ValueError("Discount must be between 0 and 1")
return round(price * (1 - discount), 2)
La differenza non è nella sintassi, ma nella gestione dell’incertezza. Il codice non è più una risposta, diventa una dichiarazione di intenti verificabili. Ed è qui che emerge un’intuizione chiave, attribuita a Andrej Karpathy, che meriterebbe più attenzione nei board tecnologici che nei thread su X: “gli LLM sono eccezionalmente bravi a iterare fino a raggiungere obiettivi specifici; non dire cosa fare, definisci il successo e lascia che convergano”. Una frase apparentemente semplice, che in realtà smonta anni di cultura imperativa nello sviluppo software.
Il secondo principio, la semplicità, è spesso celebrato ma raramente praticato. L’intelligenza artificiale ha una tendenza strutturale all’overengineering. Genera codice come se stesse scrivendo per un manuale accademico, non per un sistema vivo. Introduce astrazioni premature, generalizzazioni inutili, livelli di configurazione che nessuno ha richiesto. Questo non è un difetto accidentale, è un effetto collaterale della sua formazione: ottimizzare per completezza apparente, non per efficacia reale. Il problema è che ogni riga di codice in più è una potenziale fonte di bug, latenza, ambiguità. Ridurre da duecento linee a cinquanta non è un esercizio estetico, è un atto di compressione del rischio.
Un esempio tipico mostra quanto rapidamente un sistema possa degenerare in complessità non richiesta.
# ❌ Overengineering da manuale
class DiscountCalculator:
def __init__(self, strategy="default"):
self.strategy = strategy
def apply(self, price, discount):
if self.strategy == "default":
return self._default(price, discount)
elif self.strategy == "seasonal":
return self._seasonal(price, discount)
def _default(self, price, discount):
return price * (1 - discount)
def _seasonal(self, price, discount):
return (price * (1 - discount)) * 0.9
# ✅ Semplice e sufficiente
def apply_discount(price: float, discount: float) -> float:
return price * (1 - discount)
La versione sofisticata sembra più flessibile, ma introduce complessità senza domanda. È capitale immobilizzato. Nessuno paga per una feature che non usa. Il codice semplice, al contrario, è liquido, adattabile, comprensibile. In un contesto enterprise, questa differenza si traduce direttamente in costi operativi.
Il terzo principio, quello delle modifiche chirurgiche, è forse il più difficile da accettare per un modello linguistico. L’AI non ha senso del contesto storico del codice. Non percepisce il peso delle decisioni passate, né il costo politico di modificarle. Tende quindi a “migliorare” tutto ciò che tocca, come un consulente junior troppo entusiasta. Il risultato è un disastro elegante: cambiamenti diffusi, non richiesti, che introducono instabilità in parti del sistema che funzionavano perfettamente. Un ingegnere esperto invece sviluppa una sorta di rispetto quasi conservatore per il codice esistente. Non perché sia perfetto, ma perché è vivo, integrato, testato implicitamente dal tempo.
Questo principio si manifesta chiaramente nelle code review. Il diff ideale è noioso. Breve, mirato, quasi banale. Quando un LLM interviene senza disciplina, il diff diventa un romanzo.
# ❌ Cambiamenti diffusi e non richiesti
def process_user_data(user):
# refactor inutile
name = user.get("name", "").strip().title()
age = int(user.get("age", 0))
return {"name": name, "age": age}
# ✅ Cambiamento chirurgico (solo ciò che serve)
def process_user_data(user):
name = user["name"]
age = user["age"]
return {"name": name, "age": age}
Il secondo esempio non è “migliore” in senso assoluto, è coerente con il requisito. Questo è ciò che conta. L’ottimizzazione fuori contesto è rumore.
Il quarto principio, l’esecuzione guidata da obiettivi verificabili, introduce una dimensione quasi scientifica nel processo di sviluppo. Trasforma richieste vaghe in criteri misurabili. Non “aggiungere validazione”, ma dimostrare che input invalidi vengono gestiti correttamente. Non “sistemare un bug”, ma costruire una prova che il bug esiste e poi eliminarlo. Questa logica ribalta il rapporto tra codice e verità. Il codice non è più la fonte della verità, lo è il comportamento verificato.
Qui emerge il vero potenziale degli LLM, quello che spesso viene sprecato in task banali. Non sono brillanti perché scrivono codice, ma perché iterano fino a soddisfare vincoli. Il paradigma cambia quando si passa da istruzioni imperative a obiettivi dichiarativi.
# ❌ Istruzione vaga
# "Fix the bug in division"
def divide(a, b):
return a / b
# ✅ Goal-driven execution
# Obiettivo: la funzione non deve crashare con b = 0
# Verifica: test che deve passare
def test_divide_by_zero():
try:
divide(10, 0)
assert False
except ZeroDivisionError:
assert True
# Implementazione che soddisfa il criterio
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
Questo schema abilita un loop autonomo. Il modello prova, fallisce, corregge, converge. Senza criteri, invece, si limita a produrre output plausibili. È la differenza tra apprendimento e imitazione.
La vera metrica di successo di questi principi non è estetica, è operativa. Si manifesta nei dettagli che solo un team esperto nota immediatamente. I diff diventano più piccoli, quasi minimalisti. Le rewrite diminuiscono drasticamente, perché il codice è corretto alla prima iterazione. Le domande arrivano prima dell’implementazione, non dopo il fallimento. Le pull request smettono di essere esercizi di stile e tornano a essere strumenti di progresso incrementale. È un cambiamento sottile ma radicale, che riduce il rumore organizzativo e aumenta la velocità reale, non quella percepita.
Questa disciplina si integra poi con regole specifiche di progetto, che rappresentano il livello successivo di maturità. Le organizzazioni più avanzate non si limitano a principi generali, li innestano su vincoli concreti, trasformando linee guida in sistemi operativi del codice. Il risultato è una stratificazione intelligente tra comportamento generico e contesto specifico.
## Project-Specific Guidelines
- Use TypeScript strict mode
- All API endpoints must have tests
- Follow existing error handling patterns in src/utils/errors.ts
Queste regole non sono dettagli tecnici, sono leve strategiche. Definiscono standard, riducono ambiguità, creano coerenza. Un modello senza vincoli è creativo ma inefficiente; un modello con vincoli chiari diventa uno strumento industriale.
Un aspetto interessante è che questi quattro principi, apparentemente operativi, riflettono dinamiche molto più ampie. Pensare prima di agire è una risposta alla cultura della velocità. La semplicità è una reazione alla complessità sistemica. Le modifiche chirurgiche sono una forma di rispetto per l’inerzia organizzativa. L’esecuzione guidata da obiettivi è un antidoto all’ambiguità manageriale. In altre parole, non stiamo parlando solo di codice, ma di governance. L’intelligenza artificiale amplifica le dinamiche esistenti; se l’organizzazione è caotica, il caos scala. Se è disciplinata, scala anche la disciplina.
Una frase sintetizza l’intero paradigma: l’intelligenza senza disciplina è rumore ad alta velocità. Sembra progresso, ma spesso è solo accelerazione del disordine. Le aziende che capiranno questo punto avranno un vantaggio competitivo non perché avranno modelli migliori, ma perché sapranno usarli meglio. In un mercato ossessionato dalla corsa al modello più potente, la vera differenziazione potrebbe essere molto meno glamour: chi riesce a mantenere il controllo mentre tutto il resto accelera.
Il paradosso finale è quasi ironico. Più l’intelligenza artificiale diventa capace, più diventano importanti principi che sembrano appartenere a un’epoca precedente. Pensare, semplificare, limitare, verificare. Non sono innovazioni, sono fondamentali. E come spesso accade nei sistemi complessi, il futuro non appartiene a chi aggiunge più funzionalità, ma a chi elimina ciò che non serve. Una lezione antica, ignorata ciclicamente, riscoperta ogni volta che la complessità supera la capacità umana di gestirla. Questa volta, la differenza è che la velocità del ciclo è molto più alta. E gli errori, inevitabilmente, molto più costosi.
Skill Karpathy: https://github.com/multica-ai/andrej-karpathy-skills?utm_source=www.aifire.co&utm_medium=newsletter&utm_campaign=ipo-warfare-openai-vs-anthropic&_bhlid=3e745f378c0e5e8fc4e51897c66749e8656bb512