Il lavoro con strumenti di intelligenza artificiale generativa, come ChatGPT o Blackbox, può sembrare un'opportunità imperdibile per semplificare e velocizzare il processo di sviluppo. Tuttavia, a volte, il codice generato non si adatta perfettamente alle esigenze specifiche di un progetto, richiedendo un intervento umano per correggere e ottimizzare il risultato. Nonostante l'IA possa produrre soluzioni corrette, spesso manca della capacità di comprendere il contesto e le necessità a lungo termine del progetto. È in queste situazioni che il giudizio del programmatore diventa cruciale.

La separazione delle preoccupazioni (separation of concerns) è uno dei principi fondamentali della programmazione. Esso stabilisce che le diverse parti di un software devono occuparsi di compiti distinti e non sovrapporsi. Un esempio classico di applicazione di questo principio riguarda la gestione delle connessioni al database. Se il codice generato dall'IA mescola la logica di accesso al database con la logica di business, come nella classe Questions, ciò rende il codice meno manutenibile e più difficile da estendere in futuro. Invece, la gestione delle connessioni dovrebbe essere estratta in una classe separata, dedicata esclusivamente a questo compito. Così facendo, non solo evitiamo la duplicazione del codice, ma creiamo anche un sistema più flessibile e modulare, pronto per eventuali cambiamenti futuri, come l'utilizzo di un database differente.

Quando l'IA suggerisce una soluzione complessa per un problema che potrebbe essere risolto con una semplice implementazione, è importante intervenire. La semplicità è spesso la chiave per garantire una manutenibilità migliore e per evitare il sovraccarico di funzionalità inutili. Ad esempio, quando un'architettura proposta non segue i pattern progettuali del progetto, o quando vengono ignorati fattori come la performance o le migliori pratiche di sicurezza, l'intervento umano diventa fondamentale. La soluzione suggerita potrebbe sembrare funzionante nel breve periodo, ma potrebbe non essere scalabile o sicura nel lungo periodo.

Un altro errore comune nell'uso dell'IA riguarda la gestione delle risorse. Se non vengono trattati correttamente gli errori o se non viene garantito che le connessioni al database vengano chiuse, il sistema rischia di incorrere in problemi di performance e di affidabilità. L'approccio migliore prevede l'uso di un pattern di connessione al database che isola la logica di connessione, supporta le transazioni, gestisce correttamente gli errori e consente di configurare facilmente i percorsi del database e le credenziali.

La creazione di un contesto di gestione delle connessioni come il seguente, utilizzando il modulo sqlite3 in Python, consente di centralizzare e riutilizzare il codice senza ripeterlo in ogni classe che richiede l'accesso al database. La classe DatabaseConnection, con l'uso del costruttore __enter__ e __exit__, permette di gestire automaticamente l'apertura e la chiusura delle connessioni al database, riducendo al minimo la possibilità di errori.

python
import sqlite3 class DatabaseConnection: def __init__(self, db_path): self.db_path = db_path def __enter__(self): self.connection = sqlite3.connect(self.db_path) return self.connection.cursor() def __exit__(self): self.connection.commit() self.connection.close()

L'uso di questa classe in un contesto di gestione (tramite la parola chiave with) consente di eseguire operazioni sul database in modo sicuro, senza dover gestire manualmente l'apertura e la chiusura delle connessioni, riducendo così il rischio di perdite di risorse.

Un altro aspetto fondamentale è la separazione delle preoccupazioni tra logica di business e logica di accesso ai dati. La classe Questions, ad esempio, dovrebbe concentrarsi esclusivamente sull'estrazione delle domande, mentre la gestione del database dovrebbe essere delegata alla classe DatabaseConnection. In questo modo, ogni classe ha una responsabilità ben definita, migliorando la leggibilità e la manutenibilità del codice.

Non è raro che l'IA produca codice che funziona solo nel "percorso felice", cioè nelle condizioni ideali in cui tutto va come previsto. In contesti reali, però, il codice deve essere resistente agli errori, facilmente configurabile e testabile. Questi aspetti, spesso trascurati dai modelli generativi, sono fondamentali per garantire la stabilità e l'affidabilità di un'applicazione a lungo termine. La refattorizzazione del codice generato dall'IA diventa quindi un passo cruciale nel miglioramento della qualità del software, con particolare attenzione alla gestione delle eccezioni, alla configurabilità e alla testabilità del codice.

In sintesi, quando si lavora con codice generato dall'IA, è fondamentale non accettare passivamente i suggerimenti, ma analizzare criticamente ogni soluzione proposta, intervenendo per migliorarla e adattarla alle esigenze del progetto. La chiave del successo risiede nel saper separare correttamente le preoccupazioni, mantenere il codice semplice e manutenibile, e garantire che ogni parte del sistema rispetti i principi fondamentali di progettazione software.

Come generare e gestire test efficaci con l'AI generativa per il codice

La generazione di test automatici è una pratica fondamentale per garantire che il codice funzioni correttamente in tutte le sue variabili. L’uso dell'Intelligenza Artificiale generativa sta semplificando e velocizzando enormemente questo processo. Un esempio di questo è l’utilizzo di strumenti come GitHub Copilot, Tabnine e Blackbox AI, che permettono di creare test per classi di codice complesse come la classe Questions in Python, con il minimo sforzo e con un livello di precisione elevato.

Quando si sviluppa un'applicazione, è fondamentale scrivere test che possano garantire che ogni componente funzioni come previsto. Tuttavia, scrivere test manualmente per ogni singolo caso può essere un compito arduo e soggetto a errori. È qui che gli strumenti di AI possono intervenire, generando automaticamente il codice di test basato sulle specifiche di progetto. Questi strumenti, seppur non perfetti, possono ridurre notevolmente il tempo e l'impegno necessari per sviluppare test completi e accurati.

Il processo che segue è un esempio di come Blackbox AI, uno degli strumenti più recenti in questo campo, possa essere utilizzato per generare test. Quando viene creato un test con Blackbox, si fornisce una descrizione del comportamento atteso del codice e lo strumento genera automaticamente il framework di test, che poi può essere raffinato dal programmatore. In questo caso, è stato generato un set di test che verifica il funzionamento della classe Questions, la quale gestisce le domande di un'applicazione. Ad esempio, testando la corretta restituzione di domande non ancora risposte o verificando la gestione delle risposte errate, gli sviluppatori possono essere certi che la logica del codice sia effettivamente testata.

Per esempio, quando si usa Blackbox AI per generare test unitari per una classe che interagisce con un database SQLite, la configurazione di un database temporaneo in memoria permette di evitare di influire sui dati di produzione. Questo approccio riduce i rischi di modifiche non desiderate sul database reale durante il testing.

L'uso di uno strumento come Blackbox AI rende tutto questo più semplice, grazie alla sua capacità di creare scaffolding di test efficaci in pochi passaggi. Il prompt iniziale, che può essere generico come "Crea test pytest per la classe Questions", viene perfezionato affinché lo strumento comprenda meglio le specifiche dell'ambiente di sviluppo, come nel caso di un'applicazione basata su un database SQLite in memoria. Questo permette di ottenere risultati migliori, adattati alle necessità specifiche del progetto, grazie all'uso di un agente specializzato, come ad esempio un "Python agent" o un "Flask agent", che aiuta a generare test specifici per quel linguaggio e quell'ambiente di sviluppo.

Un altro punto importante riguarda il controllo della qualità del codice generato. Anche se gli strumenti di AI come Blackbox AI sono efficaci, è necessario fare un passo indietro e rivedere attentamente il codice generato. Alcuni test potrebbero essere incompleti o non adattarsi perfettamente al contesto del software. In tal caso, è fondamentale avere una buona conoscenza del proprio codice e della logica sottostante per rivedere o aggiungere nuovi test manualmente.

Quando si scrivono test con l'aiuto dell'AI, è consigliabile seguire una serie di pratiche per ottenere i migliori risultati. La generazione dei test dovrebbe iniziare con la creazione di uno scaffolding di base, che includa i fixture e gli import necessari, e poi i test dovrebbero essere aggiunti progressivamente, un tipo di test alla volta. È fondamentale rivedere le assunzioni fatte dallo strumento, verificare i casi limite e affinare le asserzioni per garantire che i test siano corretti e completi. È anche importante consolidare i fixture, cercando opportunità di riutilizzare le configurazioni del test.

L'AI può generare rapidamente test per i casi comuni, ma è il programmatore a dover garantire che vengano coperti anche tutti i casi particolari e le eccezioni che potrebbero verificarsi durante l'esecuzione dell'applicazione. L'AI si dimostra quindi un ottimo alleato, ma solo se utilizzato in collaborazione con una conoscenza approfondita del proprio codice e dei comportamenti attesi.

Concludendo, l'utilizzo degli strumenti di AI generativa permette agli sviluppatori di risparmiare tempo e di mantenere un alto standard di qualità nel codice, generando test efficaci e coprendo una vasta gamma di scenari di test. Tuttavia, è importante non perdere di vista la necessità di rivedere, perfezionare e aggiungere manualmente quei test che l'AI potrebbe non essere in grado di generare da solo.