Il problema con la maggior parte degli articoli sull'AI
Leggo ogni settimana decine di articoli che inneggiano all'AI come rivoluzione assoluta dello sviluppo software. Cambierà tutto, sostituirà i programmatori, farà tutto da sola. Trovo questi pezzi poco utili, perché saltano la domanda concreta che mi pongo ogni mattina davanti a un progetto: in questo specifico caso, ha senso usarla?
La risposta non è sempre sì. E saperlo distinguere è esattamente ciò che separa un uso intelligente dello strumento da un uso acritico che, alla lunga, genera dipendenza senza costruire competenza.
In questo articolo non ti parlerò di AI in astratto. Ti racconterò un caso reale su cui sto lavorando, il ragionamento tecnico che mi ha portato a coinvolgere l'AI, e il vantaggio concreto che ne ho ricavato.
Il contesto: un portale automotive su Drupal
Sto lavorando allo sviluppo front-end di un portale di settore automotive che gira su Drupal. Tra i vari interventi in corso, mi è stato chiesto di migliorare la UI della sezione commenti: il problema segnalato dal project manager era chiaro, i commenti annidati — cioè le risposte dirette a un commento — non erano visivamente distinguibili in modo netto dal flusso generale della discussione.
Un utente che scorreva la pagina faticava a capire quale commento fosse una risposta a quale. Il contenuto era corretto, la struttura semantica anche, ma l'esperienza di lettura era confusa.
La struttura di Drupal
In Drupal, i commenti annidati seguono uno schema preciso: ogni commento è un elemento article, e i commenti figli vengono racchiusi in un contenitore con classe .indented che segue immediatamente l'article padre. La struttura nel DOM è quindi questa:
html
<!-- Commento padre --> <article class="comment"> <!-- Contenuto del commento padre --> </article> <!-- Commenti figli --> <div class="indented"> <article class="comment"> <!-- Contenuto del primo commento figlio --> </article> <article class="comment"> <!-- Contenuto del secondo commento figlio --> </article> </div>Visivamente, con il solo CSS di default, non c'era nulla che collegasse in modo evidente il commento padre ai suoi figli. Serviva un intervento.
Il ragionamento tecnico prima dell'AI
Prima di pensare a qualsiasi soluzione — con o senza AI — ho analizzato il problema dal punto di vista del CSS. L'obiettivo era aggiungere una linea verticale in stile timeline che collegasse visivamente il commento padre ai commenti figli, simile all'approccio adottato da forum e sistemi di commento moderni come quelli di Reddit o GitHub.
Perché il CSS da solo non bastava
Il CSS moderno è potente, ma ha un limite noto a chiunque lavori seriamente con i selettori: è possibile selezionare un elemento se preceduto da un elemento specifico — è quello che fa il selettore adiacente + o il selettore generale ~ — ma non è possibile selezionare un elemento se seguito da un elemento specifico.
In termini pratici: avrei potuto applicare uno stile all'article padre solo se avessi già saputo che dopo di lui c'è un .indented. Ma il CSS non permette di guardare avanti nel DOM. Quindi, per applicare la linea verticale solo ai commenti che hannofigli, avrei avuto bisogno di un selettore inesistente.
Esistono proposte in discussione nel W3C per un selettore :has() con lookup verso i successivi, ma la copertura browser per casi complessi rimane limitata e comunque il problema strutturale qui era diverso: non si trattava solo di stilizzare il padre, ma di creare un wrapper visivo che unisse padre e figli in un unico blocco identificabile.
La soluzione: JavaScript per modificare il DOM
La strada corretta era usare JavaScript per identificare le coppie article + .indented e racchiuderle in un wrapper. Il wrapper avrebbe poi ricevuto lo stile CSS con la linea verticale. Risultato: visivamente compatto, semanticamente corretto, facilmente manutenibile.
La logica era chiara. L'implementazione, però, richiedeva attenzione: bisognava scorrere il DOM, trovare ogni articleseguito da un .indented, creare un elemento wrapper, spostare i nodi al suo interno senza rompere la struttura, e gestire i casi in cui i commenti figli fossero assenti. Fatto con cura, quel lavoro avrebbe richiesto circa un'ora — non per difficoltà intrinseca, ma per la normale sequenza di scrittura, test, correzione e rifinitura.
Quando ha senso coinvolgere l'AI
Ed è qui che ho applicato un ragionamento che uso spesso: qual è il costo opportunità di scrivere questo codice da solo?
Trattandosi di uno script di manipolazione DOM relativamente standard — nessuna logica di business complessa, nessuna integrazione con API, nessun requisito di sicurezza critico — il tempo necessario era certo ma non giustificato dalla complessità del problema. L'AI eccelle esattamente in questi casi: compiti ben definiti, output verificabile, nessuna ambiguità sul risultato atteso.
Ho descritto il problema in modo preciso: struttura HTML di partenza, obiettivo (creare wrapper attorno alle coppie article + .indented), vincoli (non rompere la struttura Drupal, gestire casi senza figli). Lo script è stato generato in pochi secondi.
Ecco una versione semplificata di quanto ottenuto e integrato, con i miei commenti aggiunti:
javascript
function wrapNestedCommentPairs(container) {
const isTopLevel = container.matches('.node__commenti__lista');
let child = container.firstElementChild;
while (child) {
const next = child.nextElementSibling;
if (isTopLevel
&& child.matches('article.node__commenti__voce')
&& next
&& next.matches('div.indented')
&& !child.closest('.node__commenti__voce__annidata')) {
const wrapper = document.createElement('div');
wrapper.className = 'node__commenti__voce__annidata';
container.insertBefore(wrapper, child);
wrapper.appendChild(child);
wrapper.appendChild(next);
wrapNestedCommentPairs(next);
child = wrapper.nextElementSibling;
} else {
if (child.matches('div.indented, .node__commenti__voce__annidata')) {
wrapNestedCommentPairs(child);
}
child = child.nextElementSibling;
}
}
}
function nestIndentedInParentElemento(container) {
let child = container.firstElementChild;
while (child) {
const next = child.nextElementSibling;
if (child.matches('div.indented')) {
const previous = child.previousElementSibling;
if (previous && previous.matches('div.node__commenti__elemento')) {
previous.appendChild(child);
nestIndentedInParentElemento(child);
child = next;
continue;
}
nestIndentedInParentElemento(child);
} else if (child.matches('.node__commenti__voce__annidata, div.node__commenti__elemento')) {
nestIndentedInParentElemento(child);
}
child = next;
}
}
function wrapCommentVoceArticles(lista) {
lista.querySelectorAll('article.node__commenti__voce').forEach((article) => {
const parent = article.parentElement;
if (!parent || parent.classList.contains('node__commenti__elemento')) {
return;
}
const wrapper = document.createElement('div');
wrapper.className = 'node__commenti__elemento';
parent.insertBefore(wrapper, article);
wrapper.appendChild(article);
});
}
function hasDirectReplies(elemento) {
return Boolean(elemento && elemento.querySelector(':scope > div.indented'));
}
function markCommentStateClasses(lista) {
lista.querySelectorAll('article.node__commenti__voce').forEach((article) => {
article.classList.remove('node__commenti__voce__genitore', 'node__commenti__voce__singola');
if (hasDirectReplies(article.parentElement)) {
article.classList.add('node__commenti__voce__genitore');
}
});
Array.from(lista.children).forEach((child) => {
if (!child.matches('div.node__commenti__elemento')) {
return;
}
const article = child.querySelector(':scope > article.node__commenti__voce');
if (article && !hasDirectReplies(child)) {
article.classList.add('node__commenti__voce__singola');
}
});
}
function collectCommentLists(context) {
const lists = [];
if (context.matches?.('.node__commenti__lista')) {
lists.push(context);
} else {
lists.push(...context.querySelectorAll('.node__commenti__lista'));
const parentList = context.closest?.('.node__commenti__lista');
if (parentList) {
lists.push(parentList);
}
}
return [...new Set(lists)];
}
function wrapNestedComments(context) {
if (!context || typeof context.querySelectorAll !== 'function') {
return;
}
collectCommentLists(context).forEach((lista) => {
wrapNestedCommentPairs(lista);
wrapCommentVoceArticles(lista);
nestIndentedInParentElemento(lista);
markCommentStateClasses(lista);
});
}
wrapNestedComments(document);
Drupal.behaviors.automotiveNestedComments = {
attach: function (context) {
wrapNestedComments(context);
}
};Il CSS applicato al wrapper comment-thread aggiunge poi la linea verticale a sinistra, creando l'effetto timeline:
css
.node__commenti__lista {
article { padding: var(--box-padding); position: relative; }
// Timeline per i commenti annidati
.indented { padding-left: 20px;
article { position: relative;
&::before, &::after { background-color: var(--border-gray); content: ''; position: absolute; }
&::before { height: 1px; left: 6px; top: 42px; width: 10px; }
&.node__commenti__voce__genitore::after { height: calc(100% - 13px); left: 20px; top: 66px; width: 1px; }
.node__commenti__voce__avatar {
&::before { aspect-ratio: 1; background-color: var(--border-gray); border-radius: 50%; content: ''; height: 7px; left: -23px; position: absolute; top: 19px; width: 7px; z-index: 0; }
}
}
& > .node__commenti__elemento:not(:last-child) { position: relative;
&::after { background-color: var(--border-gray); content: ''; height: calc(100% + 5px); left: 0; position: absolute; top: 48px; width: 1px; z-index: 0; }
}
}
.node__commenti__voce__annidata {
& > .node__commenti__elemento > article { position: relative;
&::after { background-color: var(--border-gray); content: ''; height: calc(100% - 13px); left: 20px; position: absolute; top: 66px; width: 1px; }
}
}
}Lo script è stato testato, ha funzionato al primo tentativo su struttura reale, e ho apportato solo un aggiustamento minore al selettore per adattarlo alla gerarchia specifica del tema Drupal in uso. Tempo totale: circa dieci minuti invece di un'ora.
Il secondo caso: esplorare soluzioni UX senza dipendere dal back-end
Nello stesso progetto, l'AI mi sta aiutando in un modo diverso ma ugualmente concreto: mi permette di prototipare e testare miglioramenti UX lato front-end senza dover coinvolgere il back-end developer a ogni iterazione.
Chi ha lavorato su progetti con team distribuiti conosce bene il problema: hai un'idea per migliorare l'interfaccia, ma per testarla devi aprire una richiesta, aspettare disponibilità, ricevere dati o endpoint, integrare, testare. Il ciclo può richiedere giorni.
Usando l'AI per generare dati mock strutturati, componenti JavaScript di test, o per eseguire rapidamente variazioni dell'interfaccia, riesco a validare le idee prima ancora di portarle al team. Se l'idea funziona visivamente e sul piano dell'esperienza utente, la richiesta al back-end arriva già definita, con specifiche precise. Se non funziona, non ho fatto perdere tempo a nessuno.
Questo approccio ha un nome nel mondo dello sviluppo: shift left, anticipare la validazione il prima possibile nel processo. L'AI lo rende praticabile anche per il front-end, non solo per i test automatizzati.
Quando l'AI non è la risposta giusta
Sarebbe sbagliato concludere questo articolo senza essere altrettanto esplicito sui limiti.
Architettura e decisioni strutturali. Quando devo decidere come organizzare un tema WordPress, come strutturare i componenti, come gestire le dipendenze tra moduli, l'AI non è il punto di partenza. Può suggerire, può mostrare pattern, ma la responsabilità della decisione richiede una comprensione del contesto che solo chi conosce il progetto ha.
Codice critico con implicazioni di sicurezza. Script che gestiscono autenticazione, validazione lato client su dati sensibili, integrazione con API esterne: in questi casi il codice generato dall'AI va esaminato riga per riga. Non è uno scetticismo irrazionale — è la stessa attenzione che si deve a qualsiasi libreria di terze parti.
Problemi che non sai ancora descrivere. L'AI funziona bene quando il problema è definito con precisione. Se stai ancora cercando di capire qual è il vero problema, usarla troppo presto ti porta verso soluzioni rapide per il problema sbagliato. Il momento di coinvolgerla è dopo aver fatto il ragionamento, non al suo posto.
Apprendimento. Se stai imparando un linguaggio, una tecnica, un framework, scrivere il codice da solo — anche lentamente, anche con errori — è parte del processo. Delegare quella fase all'AI significa privarsi dell'esperienza che costruisce competenza reale.
Cosa cambia concretamente nel flusso di lavoro
L'integrazione dell'AI nel mio processo quotidiano non ha cambiato il modo in cui penso ai problemi. Ha cambiato la velocità con cui passo dalla soluzione al codice funzionante su alcuni tipi specifici di compiti.
Il mio flusso attuale è questo:
- Analisi del problema — sempre manuale, sempre prima.
- Identificazione dell'approccio — quale tecnologia, quale pattern, quali vincoli.
- Valutazione del costo — quanto tempo richiederebbe scrivere questo da solo?
- Decisione — se il problema è ben definito e il codice è verificabile, coinvolgo l'AI. Altrimenti scrivo.
- Revisione critica — il codice generato non viene mai usato senza lettura. Mai.
Questo processo non è diverso da quello che si usa con le librerie open source: le si usa quando risolvono un problema specifico meglio di quanto faresti tu, e si verifica sempre cosa fanno.
Conclusioni
L'AI è uno strumento. Come il CSS, come JavaScript, come qualsiasi libreria: ha un dominio di applicazione in cui è molto efficace e uno in cui non aggiunge valore o può causare danni.
Nel caso dei commenti Drupal che ho descritto, il guadagno di produttività è stato reale e misurabile. Il ragionamento tecnico era mio — ho identificato il limite del CSS, ho scelto l'approccio JavaScript, ho definito i requisiti con precisione. L'AI ha accelerato la fase di implementazione di un compito ben circoscritto.
È questa la distinzione che conta: l'AI amplifica chi sa già cosa sta facendo. Non sostituisce il ragionamento tecnico, lo accelera — ma solo quando quel ragionamento è già avvenuto.