Python per il prototipo: perché funziona benissimo
Python è lo strumento giusto per l'esplorazione e la ricerca. Il motivo è semplice: la velocità di scrittura del codice supera quella di qualsiasi alternativa per workload di data science, ML e analisi dati.
- Ecosistema ML/AI insostituibile: NumPy, Pandas, scikit-learn, PyTorch, TensorFlow, LangChain, Hugging Face. Non esiste alternativa paragonabile per l'esplorazione di modelli.
- Sintassi concisa: un ricercatore o un data scientist può concentrarsi sul problema, non sul linguaggio. Meno boilerplate, più sperimentazione.
- REPL e notebook: Jupyter permette di esplorare i dati in modo interattivo, visualizzare i risultati, documentare il ragionamento nel codice. Fondamentale per il ciclo hypothesis → test → result.
- Prototipazione rapida: da zero a "funziona" in ore invece di giorni. Per validare un'idea, non esiste niente di meglio.
Per il prototipo, per la ricerca, per l'analisi esplorativa: Python è la risposta giusta. Non c'è discussione.
Python in produzione: i problemi strutturali
Il problema non è Python in sé. È Python usato per scopi per cui non è stato progettato — sistemi enterprise che devono gestire decine di migliaia di richieste al giorno, essere mantenuti da team di 5-10 persone per 5+ anni, e non fallire silenziosamente.
Il GIL (Global Interpreter Lock)
Python non esegue thread in parallelo reale. Il GIL garantisce che solo un thread alla volta esegua bytecode Python. Per applicazioni CPU-bound o per server che devono gestire molte richieste concorrenti, questo è un collo di bottiglia strutturale. I workaround esistono (multiprocessing, asyncio, worker esterni) ma aumentano la complessità architetturale e il costo operativo.
Tipo dinamico: la bomba a orologeria
In Python, un bug di tipo — passare una stringa dove si aspetta un intero, restituire None dove si aspetta un oggetto — emerge solo a runtime. In produzione. Magari alle 3 di notte di un sabato. Type hints e mypy aiutano, ma non sono enforced dal runtime e non eliminano il problema in sistemi complessi con molti contributor.
Dependency hell
pip, virtualenv, conda, poetry — la gestione delle dipendenze Python è migliorata, ma la gestione delle transitive dependencies rimane fragile. Riprodurre esattamente l'ambiente di produzione su una macchina diversa è un'impresa. E le dipendenze di librerie ML pesanti cambiano spesso in modo incompatibile.
Mancanza di struttura scalabile
Python permette di scrivere codice in modi molto diversi. Questo è un vantaggio in fase esplorativa. In un team di 8 persone su una base di codice di 100k righe senza un'architettura esplicita, diventa caos. La mancanza di interfacce esplicite, la tipizzazione dinamica e la cultura "funziona finché funziona" portano a sistemi impossibili da refactoring sicuro.
Perché Java vince in produzione
Java non è il linguaggio trendy degli ultimi anni. È il linguaggio su cui girano la maggior parte dei sistemi bancari, delle piattaforme di e-commerce, dei sistemi ERP e delle applicazioni enterprise nel mondo. C'è una ragione precisa.
| Criterio | Python | Java (Spring Boot) |
|---|---|---|
| Velocità di prototipazione | Eccellente | Media |
| Bug rilevati pre-runtime | Pochi (mypy aiuta) | Molti (compilatore + IDE) |
| Performance throughput | Limitata da GIL | Alta (JIT + virtual thread) |
| Concorrenza | asyncio (complesso) | Virtual thread (Java 21) |
| Manutenibilità a 3 anni | Difficile senza disciplina | Ottima con Spring |
| Ecosistema ML/AI | Insostituibile | Limitato (DJL, ONNX) |
| Onboarding sviluppatori | Facile | Medio (più struttura) |
| Refactoring sicuro | Rischioso | Sicuro (tipi statici) |
Tipizzazione statica: il compilatore trova i bug prima del runtime. In sistemi complessi con molti sviluppatori, questo equivale a meno bug in produzione e refactoring possibile senza paura.
Spring Boot e l'ecosistema enterprise: dependency injection, gestione transazionale, security, integrazione con qualsiasi database, tool di monitoraggio (Actuator, Micrometer), test coverage reale con JUnit. L'ecosistema esiste, è maturo e non introduce dipendenze fragili.
Java 21 e le virtual thread: Project Loom ha risolto il problema storico di Java con la concorrenza ad alto throughput. Le virtual thread permettono di gestire migliaia di connessioni concorrenti con codice sincrono semplice da leggere e debuggare.
Il pattern più pericoloso: nasce un PoC in Python, funziona, il management lo vede, decide di mandarlo in produzione "tanto già c'è". Sei mesi dopo il sistema è in produzione ma non è scalabile, non ha monitoring, non ha error handling, non è testato. E riscriverlo costerà il doppio di averlo scritto bene dall'inizio. Questo non è un caso limite — è il pattern più comune che vediamo.
La soluzione: esplicitare il confine
La soluzione non è non fare PoC in Python. La soluzione è essere espliciti sul confine: il PoC è un esperimento, non un prodotto. Ha una vita definita. Quando l'esperimento valida l'idea di business, si costruisce il prodotto con gli strumenti giusti — con i tempi e i costi corretti già previsti nel budget.
Un PoC Python che dimostra che l'idea funziona ha valore inestimabile. Lo stesso codice mandato in produzione senza riscrittura è un debito tecnico che si paga con gli interessi.
Come lavoriamo in Blueware
Usiamo Python per la fase di ricerca e prototipazione: modelli ML, pipeline di analisi dati, script di data engineering, validazione di ipotesi. Quando il PoC ha validato il valore, costruiamo il sistema di produzione in Java Spring Boot: API REST documentate (OpenAPI), database relazionale con schema versionato (Flyway), test coverage reale, monitoring integrato.
Questo non è dogmatismo linguistico: è rispetto per il cliente. Un sistema che deve funzionare in produzione per cinque anni, essere manutenuto da un team che nel tempo cambierà, e scalare con il business — merita una base solida. Python per esplorare. Java per costruire.