Creo applicazioni web moderne e strumenti digitali personalizzati per aiutare le attività a crescere attraverso l'innovazione tecnologica. La mia passione è unire informatica ed economia per generare valore reale.
La mia passione per l'informatica è nata tra i banchi dell'Istituto Tecnico Commerciale di Maglie, dove ho scoperto il potere della programmazione e il fascino di creare soluzioni digitali. Fin da subito, ho capito che l'informatica non era solo codice, ma uno strumento straordinario per trasformare idee in realtà.
Durante gli studi superiori in Sistemi Informativi Aziendali, ho iniziato a intrecciare informatica ed economia, comprendendo come la tecnologia possa essere il motore della crescita per qualsiasi attività. Questa visione mi ha accompagnato all'Università degli Studi di Bari, dove ho conseguito la Laurea in Informatica, approfondendo le mie competenze tecniche e la mia passione per lo sviluppo software.
Oggi metto questa esperienza al servizio di imprese, professionisti e startup, creando soluzioni digitali su misura che automatizzano processi, ottimizzano risorse e aprono nuove opportunità di business. Perché la vera innovazione inizia quando la tecnologia incontra le esigenze reali delle persone.
Le Mie Competenze
Analisi Dati & Modelli Previsionali
Trasformo i dati in insights strategici con analisi approfondite e modelli predittivi per decisioni informate
Automazione Processi
Creo strumenti personalizzati che automatizzano operazioni ripetitive e liberano tempo per attività a valore aggiunto
Sistemi Custom
Sviluppo sistemi software su misura, dalle integrazioni tra piattaforme alle dashboard personalizzate
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Il mio percorso accademico e le tecnologie che padroneggio
Certificazioni Professionali
8 certificazioni conseguite
Nuovo
Visualizza
Reinvention With Agentic AI Learning Program
Anthropic
Dicembre 2024
Nuovo
Visualizza
Agentic AI Fluency
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency for Students
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency: Framework and Foundations
Anthropic
Dicembre 2024
Nuovo
Visualizza
Claude with the Anthropic API
Anthropic
Dicembre 2024
Visualizza
Master SQL
RoadMap.sh
Novembre 2024
Visualizza
Oracle Certified Foundations Associate
Oracle
Ottobre 2024
Visualizza
People Leadership Credential
Connect
Settembre 2024
Linguaggi & Tecnologie
Java
Python
JavaScript
Angular
React
TypeScript
SQL
PHP
CSS/SCSS
Node.js
Docker
Git
💼
12/2024 - Presente
Custom Software Engineering Analyst
Accenture
Bari, Puglia, Italia · Ibrida
Analisi e sviluppo di sistemi informatici attraverso l'utilizzo di Java e Quarkus in Health and Public Sector. Formazione continua su tecnologie moderne per la creazione di soluzioni software personalizzate ed efficienti e sugli agenti.
💼
06/2022 - 12/2024
Analista software e Back End Developer Associate Consultant
Links Management and Technology SpA
Esperienza nell'analisi di sistemi software as-is e flussi ETL utilizzando PowerCenter. Formazione completata su Spring Boot per lo sviluppo di applicazioni backend moderne e scalabili. Sviluppatore Backend specializzato in Spring Boot, con esperienza in progettazione di database, analisi, sviluppo e testing dei task assegnati.
💼
02/2021 - 10/2021
Programmatore software
Adesso.it (prima era WebScience srl)
Esperienza nell'analisi AS-IS e TO-BE, evoluzioni SEO ed evoluzioni website per migliorare le performance e l'engagement degli utenti.
🎓
2018 - 2025
Laurea in Informatica
Università degli Studi di Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Sistemi Informativi Aziendali
Istituto Tecnico Commerciale di Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
07 - Debugging con Cursor AI: 3x Più Veloce
Il debugging e una delle attivita che consuma più tempo nella vita di un developer. Studi interni
di Microsoft e Google riportano che i developer senior trascorrono tra il 30 e il 40 percento del
loro tempo produttivo a cercare e risolvere bug. Con l'avvento degli AI-native IDE, questa
percentuale sta crollando. Chi usa Cursor con un workflow ottimizzato riporta riduzioni fino
al 65 percento del tempo speso in debugging, non perchè l'AI sia infallibile, ma perchè
trasforma un processo solitario e spesso frustrante in una collaborazione strutturata e sistematica.
Questo articolo non si limita a mostrare come "chiedere all'AI di fixare un bug". Esploriamo
le funzionalità specifiche di Cursor dedicate al debugging, dalla Debug Mode introdotta
con Cursor 2.2 al sistema Bugbot per la revisione proattiva dei PR,
passando per strategie avanzate su memory leaks, race conditions e flaky tests in TypeScript
e Angular. Imparerai a costruire un workflow ripetibile che trasforma il debugging da arte
oscura a processo ingegneristico verificabile.
Cosa Imparerai in Questo Articolo
Come funziona la Debug Mode di Cursor 2.2 e il suo agent loop basato su runtime logs
Bugbot: rilevamento proattivo dei bug su pull request con autofix automatico
Workflow Cmd+K e chat inline per stack trace analysis istantanea
Configurare il debugger TypeScript/Angular con source maps in Cursor
Strategie AI per memory leaks, race conditions e flaky tests
Integrazione con Chrome DevTools e il debugger VS Code nativo
Chiunque abbia trascorso tre ore a fissare uno stack trace per scoprire che il problema era
un undefined su un campo nullable sa esattamente di cosa stiamo parlando. Il debugging
tradizionale ha un costo cognitivo enorme: devi mantenere in testa lo stato del programma,
formulare ipotesi, aggiungere log, riprodurre il bug, interpretare i risultati e iterare.
Ogni interruzione del flusso - un nuovo log da aggiungere, un breakpoint da spostare -
spezza la concentrazione.
I developer che usano Cursor per il debugging non eliminano questo processo, ma lo accelerano
radicalmente. La chiave e che l'AI può mantenere simultaneamente in "testa" l'intero contesto
della codebase mentre tu ti concentri sulla logica ad alto livello. Cursor 2.2 ha formalizzato
questo approccio con la Debug Mode, un agent loop completamente nuovo costruito
attorno a runtime information e verifica umana.
Debugging Tradizionale vs AI-Assisted: I Numeri
Attivita
Tradizionale
Con Cursor AI
Risparmio
Analisi stack trace
15-30 min
2-5 min
~80%
Identificazione root cause
1-4 ore
20-45 min
~70%
Fix e verifica
30-90 min
10-25 min
~65%
Memory leak investigation
2-6 ore
45-90 min
~70%
Race condition detection
4-12 ore
1-3 ore
~75%
Flaky test stabilization
2-8 ore
30-60 min
~80%
Debug Mode: L'Agent Loop di Cursor 2.2
Il 10 dicembre 2025, Cursor ha rilasciato la versione 2.2 introducendo Debug Mode,
una delle feature più significative nella storia dell'IDE. Non si tratta di una semplice
integrazione del debugger VS Code con l'AI: e un agent loop completamente ridisegnato
attorno a runtime information e collaborazione umana.
Come Funziona il Debug Loop
La differenza fondamentale rispetto al debugging assistito generico e che Debug Mode non cerca
immediatamente di generare una fix. Invece, segue un processo in cinque fasi:
Il Ciclo Debug Mode
Analisi Ipotetica: Cursor legge la codebase e genera multiple ipotesi
su cosa potrebbe essere sbagliato, senza assumere di conoscere la causa
Strumentazione: L'agente aggiunge runtime logs mirati nei punti
identificati come probabili sorgenti del problema
Riproduzione Guidata: Cursor chiede all'utente di riprodurre il bug
con la strumentazione in place e raccoglie i runtime data
Fix Targetizzata: Basandosi sui dati reali, propone una fix minima
e precisa invece di una riscrittura speculativa
Verifica e Pulizia: Dopo la verifica, rimuove tutta la strumentazione
lasciando un diff pulito pronto per il commit
Per attivare Debug Mode, apri il pannello Composer, clicca sul dropdown accanto al pulsante
di invio e seleziona "Debug Mode". Da quel momento, l'agente opererà in questo loop
invece di procedere direttamente con le modifiche.
// Esempio di come Cursor strumenta il codice durante Debug Mode
// File: src/app/services/payment.service.ts
// PRIMA della strumentazione
async processPayment(order: Order): Promise<PaymentResult> {
const cart = await this.cartService.getCart(order.userId);
const total = this.calculateTotal(cart);
return this.paymentGateway.charge(order.paymentMethod, total);
}
// DOPO la strumentazione aggiunta da Cursor Debug Mode
async processPayment(order: Order): Promise<PaymentResult> {
console.log('[DEBUG:cursor] processPayment called', {
orderId: order.id,
userId: order.userId,
paymentMethod: order.paymentMethod
});
const cart = await this.cartService.getCart(order.userId);
console.log('[DEBUG:cursor] cart retrieved', {
cartId: cart?.id,
itemCount: cart?.items?.length,
cartIsNull: cart === null
});
const total = this.calculateTotal(cart);
console.log('[DEBUG:cursor] total calculated', { total, cartTotal: cart?.total });
const result = await this.paymentGateway.charge(order.paymentMethod, total);
console.log('[DEBUG:cursor] payment result', { success: result.success, error: result.error });
return result;
}
// Dopo la riproduzione, Cursor vede nei log:
// [DEBUG:cursor] cart retrieved { cartId: undefined, itemCount: undefined, cartIsNull: false }
// Questo rivela che cart esiste ma items e undefined - bug trovato!
L'approccio della strumentazione mirata e potente perchè elimina le congetture. Invece di
riscrivere codice basandosi su ipotesi, Cursor raccoglie prove concrete prima di intervenire.
Il risultato finale e sempre un diff minimo: solo la fix necessaria, senza refactoring non
richiesto o aggiunte di feature non concordate.
Quando Debug Mode e Più Efficace
Bug intermittenti che non si riproducono sempre (race conditions, timing issues)
Problemi in codice legacy che non conosci bene
Bug che emergono solo in produzione con dati reali
Errori con stack trace profondo che attraversa multiple librerie
Comportamenti inattesi senza errori espliciti (silent failures)
Debug Mode e meno utile per errori di compilazione o errori TypeScript evidenti,
dove Cmd+K inline e molto più rapido.
Bugbot: Rilevamento Proattivo dei Bug su PR
Mentre Debug Mode risolve i bug dopo che si manifestano, Bugbot tenta di
intercettarli prima ancora che arrivino in produzione. Rilasciato in versione 1 a luglio 2025
e significativamente potenziato con Bugbot v11 a gennaio 2026, si integra nel processo
di code review analizzando automaticamente ogni pull request.
Come Funziona Bugbot
Bugbot analizza il diff del PR usando multiple passate parallele, ciascuna con un ordinamento
diverso delle modifiche per stimolare ragionamenti differenti nel modello. I risultati vengono
combinati con majority voting per ridurre i falsi positivi. Questo approccio, simile a un
ensemble di reviewer umani, ha dimostrato di catturare bug reali che passano inosservati
nelle code review tradizionali.
// Esempio di configurazione Bugbot rules in .cursor/bugbot.rules
// Queste regole personalizzano cosa Bugbot considera un bug nel tuo progetto
# Regole per progetto Angular/TypeScript
## Patterns Pericolosi
- Flag qualsiasi uso di 'any' in funzioni pubbliche dei servizi
- Segnala operatori non-null assertion '!' su proprietà di input Angular
- Evidenzia subscribe() senza corrispondente unsubscribe() o takeUntilDestroyed()
- Avvisa quando una migration SQL non ha rollback corrispondente
## Invarianti del Progetto
- I componenti non devono accedere direttamente all'HttpClient, solo ai servizi
- Tutti i form devono avere validazione lato client E lato server
- Non usare localStorage direttamente, usa StorageService
## Falsi Positivi da Ignorare
- I file *.spec.ts possono usare 'any' per i mock
- I file di configurazione possono avere assert non-null
Bugbot Autofix: Risoluzione Automatica
La feature più potente di Bugbot v11 e Autofix: quando Bugbot identifica
un bug in un PR, può automaticamente aprire un Cloud Agent per implementare la fix.
L'agente propone le modifiche come commento al PR o come commit aggiuntivo, permettendo
al reviewer di accettare o rifiutare il fix con un click.
// Bug trovato da Bugbot su PR #142:
// File: src/app/components/user-profile/user-profile.component.ts
// PROBLEMA: Memory leak - Observable non viene unsubscribed
@Component({
selector: 'app-user-profile',
template: `<div>{{ user?.name }}</div>`
})
export class UserProfileComponent implements OnInit {
user: User | null = null;
constructor(private userService: UserService) {}
ngOnInit() {
// BUGBOT: Observable subscription senza unsubscribe
// Causa memory leak quando il componente viene distrutto
this.userService.getCurrentUser().subscribe(user => {
this.user = user;
});
}
}
// FIX PROPOSTO DA BUGBOT AUTOFIX:
@Component({
selector: 'app-user-profile',
template: `<div>{{ user?.name }}</div>`
})
export class UserProfileComponent {
private destroyRef = inject(DestroyRef);
user: User | null = null;
constructor(private userService: UserService) {
this.userService.getCurrentUser()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(user => {
this.user = user;
});
}
}
Bugbot e in fase di evoluzione continua. Cursor sta sperimentando una versione always-on
che scansiona continuamente la codebase invece di attendere i PR, e la prossima major version
permettera a Bugbot di eseguire il codice per verificare i suoi stessi bug report prima
di segnalarli.
Debugging Rapido con Cmd+K e Chat Inline
Per la maggior parte dei bug quotidiani, Debug Mode e Bugbot sono strumenti potenti ma
a volte sovradimensionati. Il modo più veloce per risolvere bug evidenti in Cursor e
attraverso Cmd+K (inline edit) e il pannello Chat.
Stack Trace Analysis Istantanea
Quando un'eccezione appare nella console o nel terminale, il workflow più efficiente e:
Seleziona l'intero stack trace nella console
Premi Cmd+Shift+L per aggiungere la selezione al contesto della chat
Scrivi: "Analizza questo stack trace e identifica la root cause. Mostra dove nel codice si trova il problema e proponi una fix minima."
Cursor non si limita a leggere la prima riga dell'errore. Segue i file paths nello stack,
recupera il codice rilevante dal tuo progetto e ricostruisce mentalmente la catena di chiamate
che ha causato l'eccezione.
// Stack trace reale di un'app Angular con NgRx
ERROR TypeError: Cannot read properties of undefined (reading 'items')
at CartComponent.getItemCount (cart.component.ts:47:35)
at CartComponent_Template (cart.component.html:12:18)
at executeTemplate (core.mjs:11223)
at refreshView (core.mjs:11094)
at refreshComponent (core.mjs:12147)
at refreshChildComponents (core.mjs:11060)
// Cosa chiedi a Cursor:
// "Questo stack trace viene da un'app Angular con NgRx.
// CartComponent.getItemCount accede a items su una proprietà
// che risulta undefined. Analizza cart.component.ts e lo store
// corrispondente per trovare dove il dato non viene inizializzato."
// Cursor risponde analizzando:
// 1. cart.component.ts - come viene acceduto lo store
// 2. cart.reducer.ts - lo stato iniziale del reducer
// 3. cart.selectors.ts - i selettori usati nel componente
// E identifica:
// PROBLEMA in cart.reducer.ts:
const initialState: CartState = {
// 'items' manca! Lo stato iniziale non ha la proprietà
loading: false,
error: null
// items: [] <-- questa riga manca
};
// FIX:
const initialState: CartState = {
items: [], // aggiunto
loading: false,
error: null
};
Cmd+K per Fix Inline Contestuali
Quando hai già identificato la linea problematica, Cmd+K e il modo più
veloce per applicare una fix. Seleziona il codice problematico, premi Cmd+K e descrivi
il problema in linguaggio naturale. Cursor modifica solo il codice selezionato, senza
toccare il resto del file.
// Selezioni questo codice in un service Angular e premi Cmd+K:
getUserData(id: string) {
return this.http.get(`/api/users/
#123;id}`);
}
// Scrivi: "aggiungi gestione errori, tipizzazione forte con User interface,
// e timeout di 5 secondi"
// Cursor genera:
getUserData(id: string): Observable<User> {
return this.http.get<User>(`/api/users/#123;id}`).pipe(
timeout(5000),
catchError((error: HttpErrorResponse) => {
if (error.status === 404) {
return throwError(() => new Error(`User #123;id} not found`));
}
if (error.name === 'TimeoutError') {
return throwError(() => new Error('Request timed out'));
}
return throwError(() => new Error('Failed to load user data'));
})
);
}
Checkpoint e Rollback Durante il Debugging
Una funzionalità spesso sottovalutata durante il debugging e il sistema di Checkpoint
di Cursor. Quando stai esplorando multiple ipotesi di fix, puoi salvare lo stato corrente
della codebase come checkpoint e tornare indietro se un tentativo peggiora la situazione.
Workflow con Checkpoint
Prima di iniziare il debug di un bug complesso, salva un checkpoint
Prova la prima ipotesi di fix con Cursor
Esegui i test: se falliscono, ripristina il checkpoint con un click
Prova la seconda ipotesi partendo da uno stato pulito
Continua finchè i test passano
Questo elimina il classico problema del "ho peggiorato le cose e non so come tornare indietro",
rendendo l'esplorazione delle soluzioni molto meno rischiosa.
Configurare il Debugger TypeScript/Angular in Cursor
Cursor utilizza lo stesso motore di debugging di VS Code, il che significa che tutti i
launch.json che conosci funzionano identicamente. La differenza e che Cursor
aggiunge un layer AI attorno al processo per aiutarti a interpretare cosa sta succedendo
e a fixare i problemi più velocemente.
Setup Source Maps per TypeScript
Il requisito fondamentale per un debugging TypeScript efficace e la configurazione corretta
delle source maps. Senza di esse, il debugger punta ai file JavaScript compilati invece
che ai file TypeScript sorgente, rendendo impossibile leggere il codice durante il debug.
// tsconfig.json - configurazione base per debugging
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"sourceMap": true, // OBBLIGATORIO per debugging TS
"inlineSources": true, // Include sorgente nelle source maps
"inlineSourceMap": false, // Usa file .map separati
"outDir": "./dist",
"strict": true,
"moduleResolution": "node"
}
}
Una volta configurato launch.json, puoi avviare il debugger con F5
come in VS Code. La differenza con Cursor e che puoi selezionare un breakpoint o un'intera
funzione nel pannello debug e chiedere all'AI di spiegare il suo comportamento, o di
suggerire perchè una variabile ha un valore inatteso.
Trick: Disabilitare TypeScript Nativo di Cursor
In progetti grandi (monorepos, workspaces con 100+ moduli), il TypeScript Language Server
di Cursor può consumare molta memoria. Se noti lentezza o crash, aggiungi questa
configurazione per usare il TypeScript del tuo progetto invece di quello interno a Cursor:
Memory Leaks, Race Conditions e Flaky Tests con AI
Queste tre categorie di bug sono storicamente le più difficili da debuggare perchè
non sono deterministiche. Si manifestano in modo intermittente, spesso solo in produzione
o sotto carico, e i tentativi di isolarle spesso li eliminano. L'AI di Cursor porta
un vantaggio significativo proprio qui: può analizzare il codice staticamente per
identificare pattern che portano a questi bug, anche prima che si manifestino.
Memory Leaks in Angular: Diagnosi e Fix
Il memory leak più comune in Angular e la subscription RxJS non gestita. Ma esistono
molti altri pattern problematici che Cursor impara a riconoscere nel tuo specifico progetto.
// Prompt efficace per ricerca memory leak:
// "Analizza questo componente e identifica tutti i possibili
// memory leaks. Includi: subscriptions non gestite, event listeners
// non rimossi, interval/timeout non cancellati, e reference circolari."
// Componente con multipli leak:
@Component({
selector: 'app-dashboard',
template: `...`
})
export class DashboardComponent implements OnInit {
data: any[] = [];
private subscription: Subscription;
constructor(
private dataService: DataService,
private router: Router,
private renderer: Renderer2,
private elementRef: ElementRef
) {}
ngOnInit() {
// LEAK 1: Subscription senza unsubscribe
this.subscription = this.dataService.getStream()
.subscribe(data => this.data = data);
// LEAK 2: Event listener sul document non rimosso
document.addEventListener('keydown', this.handleKeyPress.bind(this));
// LEAK 3: Interval non cancellato
setInterval(() => this.refreshData(), 5000);
// LEAK 4: Router events subscription
this.router.events.subscribe(event => {
console.log(event);
});
}
handleKeyPress(event: KeyboardEvent) { /* ... */ }
refreshData() { /* ... */ }
}
// VERSIONE CORRETTA (generata da Cursor):
@Component({
selector: 'app-dashboard',
template: `...`
})
export class DashboardComponent implements OnInit, OnDestroy {
data: any[] = [];
private destroyRef = inject(DestroyRef);
private intervalId: ReturnType<typeof setInterval> | null = null;
private boundHandleKeyPress: (event: KeyboardEvent) => void;
constructor(
private dataService: DataService,
private router: Router
) {}
ngOnInit() {
// FIX 1: takeUntilDestroyed per tutte le subscriptions
this.dataService.getStream()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => this.data = data);
// FIX 2: Listener con cleanup esplicito
this.boundHandleKeyPress = this.handleKeyPress.bind(this);
document.addEventListener('keydown', this.boundHandleKeyPress);
// FIX 3: Interval con riferimento per cleanup
this.intervalId = setInterval(() => this.refreshData(), 5000);
// FIX 4: Router events con takeUntilDestroyed
this.router.events
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => console.log(event));
}
ngOnDestroy() {
document.removeEventListener('keydown', this.boundHandleKeyPress);
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
handleKeyPress(event: KeyboardEvent) { /* ... */ }
refreshData() { /* ... */ }
}
Race Conditions: Diagnosis Pattern
Le race conditions in Angular spesso emergono quando multiple richieste HTTP asincrone
si completano in ordine imprevedibile, o quando lo stato del componente viene aggiornato
dopo che il componente e stato distrutto.
// Race condition classica in Angular: richieste HTTP concorrenti
// L'utente digita velocemente nel search box - ogni keystroke
// lancia una nuova richiesta, ma non necessariamente arrivano in ordine
// PROBLEMA:
@Component({
selector: 'app-search',
template: `
<input (input)="onSearch($event)">
<div *ngFor="let result of results">{{ result.title }}</div>
`
})
export class SearchComponent {
results: SearchResult[] = [];
constructor(private searchService: SearchService) {}
onSearch(event: Event) {
const query = (event.target as HTMLInputElement).value;
// RACE CONDITION: se digiti "an" poi "ang", la risposta per "an"
// potrebbe arrivare DOPO quella per "ang", mostrando risultati sbagliati
this.searchService.search(query).subscribe(results => {
this.results = results;
});
}
}
// SOLUZIONE con switchMap (generata da Cursor):
@Component({
selector: 'app-search',
template: `
<input [formControl]="searchControl">
<div *ngFor="let result of results$ | async">{{ result.title }}</div>
`
})
export class SearchComponent {
searchControl = new FormControl('');
private destroyRef = inject(DestroyRef);
results$ = this.searchControl.valueChanges.pipe(
debounceTime(300), // Attendi 300ms dopo l'ultimo keystroke
distinctUntilChanged(), // Ignora se il valore non cambia
filter(query => (query?.length ?? 0) >= 2), // Min 2 caratteri
switchMap(query => // Cancella la richiesta precedente
this.searchService.search(query ?? '').pipe(
catchError(() => of([])) // Gestisce errori senza rompere lo stream
)
),
takeUntilDestroyed(this.destroyRef)
);
constructor(private searchService: SearchService) {}
}
Flaky Tests: Strategie di Stabilizzazione
I flaky tests (test che passano e falliscono intermittentemente) sono uno dei problemi
più frustranti dello sviluppo moderno. Cursor e particolarmente efficace nell'identificare
le cause perchè può analizzare contemporaneamente il test, il codice testato e i pattern
di timing.
// Flaky test in Angular: problema con async/await e timing
// Questo test fallisce circa il 30% delle volte
// FLAKY TEST:
it('should update user when form is submitted', fakeAsync(() => {
const fixture = TestBed.createComponent(UserFormComponent);
const component = fixture.componentInstance;
fixture.detectChanges();
component.nameControl.setValue('John Doe');
component.onSubmit();
// Problema: il test non aspetta il completamento dell'Observable
expect(component.successMessage).toBe('User updated successfully');
}));
// Cosa chiedi a Cursor:
// "Questo test e flaky - fallisce intermittentemente. Analizza il
// componente UserFormComponent e identifica perchè il test non e
// deterministico. Proponi una versione stabile del test."
// RISPOSTA DI CURSOR - analisi del problema:
// Il componente usa un Observable con delay, il test non lo aspetta correttamente
// TEST STABILE (generato da Cursor):
it('should update user when form is submitted', fakeAsync(() => {
const userServiceSpy = jasmine.createSpyObj('UserService', ['updateUser']);
userServiceSpy.updateUser.and.returnValue(of({ success: true }).pipe(delay(100)));
TestBed.configureTestingModule({
declarations: [UserFormComponent],
providers: [
{ provide: UserService, useValue: userServiceSpy }
]
});
const fixture = TestBed.createComponent(UserFormComponent);
const component = fixture.componentInstance;
fixture.detectChanges();
component.nameControl.setValue('John Doe');
component.onSubmit();
// Avanza il tempo virtuale di 100ms per completare il delay
tick(100);
fixture.detectChanges();
expect(component.successMessage).toBe('User updated successfully');
expect(userServiceSpy.updateUser).toHaveBeenCalledOnceWith({
name: 'John Doe'
});
}));
Performance Profiling Assistito da AI
Il debugging di performance e una disciplina a parte: non si tratta di trovare errori ma di
capire perchè qualcosa e lento. Cursor non sostituisce i profiler dedicati come Chrome
DevTools o Angular DevTools, ma accelera enormemente il processo di interpretazione
dei dati di profiling e di implementazione delle ottimizzazioni.
Analizzare Profili di Performance con Cursor
Il workflow più efficace e catturare i dati di performance con i tool nativi, poi portare
i risultati in Cursor per l'analisi e l'implementazione delle fix.
// Esempio di workflow: ottimizzare un componente Angular lento
// Hai identificato con Angular DevTools che questo componente
// causa 850ms di change detection su ogni input
// Componente problematico (dati dal profiler):
@Component({
selector: 'app-large-table',
template: `
<table>
<tr *ngFor="let item of processedItems">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ formatDate(item.createdAt) }}</td>
<td>{{ calculateTotal(item) }}</td>
</tr>
</table>
`
})
export class LargeTableComponent {
@Input() items: RawItem[] = [];
// PROBLEMA 1: getter ricalcolato ad ogni change detection
get processedItems() {
return this.items.map(item => this.processItem(item));
}
// PROBLEMA 2: funzione chiamata nel template - ricreata ogni volta
formatDate(date: Date): string {
return new Intl.DateTimeFormat('it-IT').format(date);
}
// PROBLEMA 3: calcolo costoso nel template
calculateTotal(item: RawItem): number {
return item.prices.reduce((acc, price) => acc + price.amount, 0);
}
private processItem(item: RawItem): ProcessedItem { /* ... */ }
}
// VERSIONE OTTIMIZZATA (generata da Cursor con spiegazione):
@Component({
selector: 'app-large-table',
template: `
<table>
<tr *ngFor="let item of processedItems; trackBy: trackById">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.formattedDate }}</td>
<td>{{ item.total }}</td>
</tr>
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush // FIX 3: OnPush CD
})
export class LargeTableComponent {
private _items: RawItem[] = [];
processedItems: ProcessedItem[] = [];
@Input() set items(value: RawItem[]) {
this._items = value;
// FIX 1: calcola solo quando input cambia, non ad ogni CD
this.processedItems = value.map(item => this.processItem(item));
}
// FIX 2: trackBy per evitare ricreazione DOM inutile
trackById(index: number, item: ProcessedItem): string {
return item.id;
}
private dateFormatter = new Intl.DateTimeFormat('it-IT');
private processItem(item: RawItem): ProcessedItem {
return {
...item,
// Pre-calcola tutto durante la trasformazione
formattedDate: this.dateFormatter.format(item.createdAt),
total: item.prices.reduce((acc, price) => acc + price.amount, 0)
};
}
}
Integrazione con Chrome DevTools
Per debugging di performance avanzato, il workflow più potente combina Chrome DevTools
con Cursor. Cattura un profilo Performance o Memory in Chrome, esporta i dati e
chiedi a Cursor di interpretarli nel contesto della tua codebase.
// Workflow con Chrome DevTools + Cursor
// 1. In Chrome DevTools > Performance, cattura un profilo
// Identifica i frame lenti (rossi) e le funzioni costose
// 2. Copia il nome delle funzioni costose nel profile:
// "component_factory.ts:847 - ComponentFactory.create - 234ms"
// 3. Porta in Cursor:
// "Ho profilato la mia app Angular e component_factory.create
// richiede 234ms nei frame lenti. Il profiler mostra che viene
// chiamata quando l'utente cambia route. Analizza il sistema
// di routing e lazy loading e identifica dove i componenti
// vengono ricreati invece di essere riutilizzati."
// 4. Per heap snapshots (memory leaks):
// - In Chrome DevTools > Memory, prendi due heap snapshots:
// uno prima e uno dopo un'operazione sospetta
// - Confronta: Objects allocated between snapshots
// - Copia i tipi di oggetti con crescita anomala in Cursor:
// "Il heap snapshot mostra un aumento di 15MB in
// 'EventEmitter' instances dopo navigazione back/forward.
// Analizza i componenti che usano EventEmitter e identifica
// dove non vengono destroyati correttamente."
Workflow di Debugging: Reproduce, Isolate, Fix, Test
Avere singole feature potenti non basta. Il vantaggio competitivo reale viene dall'integrare
questi strumenti in un workflow ripetibile e sistematico. Ecco il workflow che i developer
più produttivi con Cursor hanno standardizzato.
Fase 1: Reproduce con Contesto Massimo
Prima di toccare il codice, raccogli tutte le informazioni disponibili sul bug. Con Cursor,
questo significa fornire all'AI il contesto più ricco possibile.
// Template per il primo messaggio di debugging in Cursor Chat:
/*
BUG REPORT:
- Comportamento atteso: [descrizione]
- Comportamento osservato: [descrizione]
- Frequenza: [sempre / intermittente al X%]
- Ambiente: [dev / staging / prod]
- Ultima modifica correlata: [commit hash o feature]
ERROR OUTPUT:
[incolla lo stack trace completo]
PASSI PER RIPRODURRE:
1. [passo 1]
2. [passo 2]
FILES PROBABILMENTE COINVOLTI:
- src/app/services/order.service.ts
- src/app/components/checkout/checkout.component.ts
@codebase analizza i file rilevanti e proponi le prime 3 ipotesi
sulla root cause, ordinate per probabilità.
*/
Fase 2: Isolate con Test Minimale
Dopo aver identificato l'ipotesi più probabile, chiedi a Cursor di creare un test
che riproduce il bug in isolamento. Questo ha due vantaggi: verifica l'ipotesi
e fornisce un test di regressione automatico.
// Prompt per creare un test di isolamento:
// "Sulla base dell'analisi, il bug sembra essere in OrderService.calculateDiscount()
// quando items ha elementi con quantity: 0. Crea un unit test Jest che riproduce
// esattamente questo scenario e verifica il comportamento corretto."
// Cursor genera:
describe('OrderService.calculateDiscount', () => {
let service: OrderService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(OrderService);
});
// Test che riproduce il bug
it('should handle items with zero quantity without throwing', () => {
const orderWithZeroQty = {
items: [
{ id: '1', price: 100, quantity: 2 },
{ id: '2', price: 50, quantity: 0 }, // caso problematico
{ id: '3', price: 75, quantity: 1 }
],
discountCode: 'SAVE10'
};
// BUG: questo throw invece di restituire 0
expect(() => service.calculateDiscount(orderWithZeroQty)).not.toThrow();
expect(service.calculateDiscount(orderWithZeroQty)).toBe(17.5);
});
});
Fase 3: Fix con Verifica Incrementale
Con il test di isolamento in place, applica la fix e verifica immediatamente.
L'approccio TDD inverso - prima il test di regressione, poi la fix - e
particolarmente efficace con Cursor perchè puoi iterare rapidamente.
// Prompt per la fix:
// "Il test di isolamento conferma il bug. Il problema e in calculateDiscount
// quando quantity e 0 causa una divisione per zero nel calcolo del prezzo
// unitario medio. Fixa calculateDiscount gestendo il caso quantity === 0
// e assicurati che tutti i test esistenti continuino a passare."
// Cursor analizza e genera la fix:
calculateDiscount(order: Order): number {
const validItems = order.items.filter(item => item.quantity > 0); // FIX
if (validItems.length === 0) {
return 0;
}
const subtotal = validItems.reduce(
(sum, item) => sum + (item.price * item.quantity),
0
);
const discount = this.discountService.getDiscount(order.discountCode);
return subtotal * (discount / 100);
}
Fase 4: Test di Regressione Completo
Dopo la fix, chiedi a Cursor di generare una suite completa di test che copre il bug
fixato e i casi limite correlati, per assicurarti che il problema non si ripresenti.
// Prompt finale:
// "La fix e applicata e il test di isolamento passa. Ora genera una suite
// completa di test per calculateDiscount che copre: items normali,
// items con quantity 0, lista vuota, discount code invalido,
// e ordini con tutti gli items a quantity 0."
// Cursor genera 8 test cases che coprono tutti gli edge cases
// e aggiunge JSDoc al metodo spiegando il comportamento atteso
Best Practices per il Debugging AI-Assisted
1. Contesto Sempre Preciso
Il debug AI-assisted e tanto efficace quanto e buono il contesto che fornisci.
La differenza tra un prompt vago e uno preciso può essere la differenza tra
10 minuti e 2 ore di lavoro.
Principi per Prompt di Debugging Efficaci
Stack trace completo: sempre 5-10 righe, non solo la prima
Comportamento atteso vs osservato: esplicita entrambi
Contesto files: usa @filename.ts per includere i file rilevanti
Ultima modifica: cita il commit o la feature se il bug e recente
Ambiente: dev/staging/prod spesso cambia il comportamento
Frequenza: sempre/intermittente/solo sotto carico
2. Non Fidarti Ciecamente delle Fix AI
L'AI può generare fix che risolvono il sintomo ma non la causa. Il workflow corretto e
sempre: capire la fix proposta, verificarla con test, e approvare consapevolmente.
Anti-Pattern: Accettare Fix Senza Capirle
Il pattern "vibe debugging" - chiedere all'AI di fixare un bug, accettare la prima
proposta senza capirla, pushare in produzione - e il modo più veloce per accumulare
debito tecnico. Ogni fix deve essere:
Capita a livello di logica
Verificata con almeno un test
Revisionata per effetti collaterali non intenzionali
3. Usare Cursor Rules per il Debugging
Puoi configurare Cursor Rules specifiche per il debugging che istruscono l'AI
sui pattern del tuo progetto, evitando fix che violano le tue convenzioni.
// .cursor/rules/debugging.mdc
---
name: Debugging Standards
globs: ["src/**/*.ts", "src/**/*.spec.ts"]
---
# Standard di Debugging per Questo Progetto
## Quando proponi un fix per un bug:
1. Spiega la root cause in massimo 3 frasi
2. Mostra PRIMA il codice problematico con commento // PROBLEMA:
3. Mostra POI la versione corretta con commento // FIX:
4. Aggiungi un test di regressione per il caso problematico
## Pattern da evitare nei fix:
- Non usare 'any' come fix temporanea per errori TypeScript
- Non aggiungere try/catch senza gestione specifica dell'errore
- Non usare '!' (non-null assertion) per silenziare errori nullable
- Non modificare i test per farli passare invece di fixare il codice
## Pattern preferiti:
- Usa Optional Chaining (?.) per accessi nullable
- Usa Nullish Coalescing (??) per default values
- Usa type guards per narrowing
- Usa catchError con specifici tipi di errore in RxJS
4. Debug Sessions Come Documentazione
Una best practice avanzata e trattare le sessioni di debug come opportunità di
documentazione. Dopo aver risolto un bug complesso, chiedi a Cursor di generare
un commento esplicativo nel codice che documenta il problema trovato e perchè
la soluzione adottata e quella corretta.
// Prompt post-fix:
// "Il bug e risolto. Aggiungi un commento JSDoc a calculateDiscount
// che documenta: il bug che abbiamo trovato (divisione per zero
// con quantity 0), la soluzione adottata (filter prima del calcolo),
// e un link al test di regressione."
// Cursor genera:
/**
* Calcola lo sconto totale per un ordine basato su un codice sconto.
*
* Nota: gli items con quantity === 0 vengono esclusi dal calcolo
* per evitare errori di divisione per zero nel calcolo del prezzo
* unitario medio. Questo comportamento e testato in:
* order.service.spec.ts > 'should handle items with zero quantity'
*
* @param order - L'ordine con items e codice sconto
* @returns Il valore dello sconto in euro (0 se nessun item valido)
*/
calculateDiscount(order: Order): number {
Strumenti e Integrazioni per il Debugging
Integrazione con Datadog
Per team che usano Datadog in produzione, l'estensione ufficiale Cursor permette di
portare i dati di production monitoring direttamente nell'IDE. Puoi aggiungere
logpoints visualizzandoli in Cursor, e chiedere all'agente di aiutarti a
troubleshootare bug in produzione basandosi su log e trace reali.
Setup Datadog + Cursor
Installa l'estensione Datadog da Cursor Extensions (compatibile VS Code)
Configura le credenziali Datadog nelle impostazioni
Nelle sessioni di debug, usa @datadog per portare log reali in contesto
Chiedi a Cursor di correlare gli errori di production con il codice sorgente
Angular DevTools e Cursor
Angular DevTools nel browser e un profiler eccellente per problemi specifici di Angular
(change detection, component tree, dependency injection). Quando trovi un problema
nel profiler, porta le informazioni in Cursor per l'analisi del codice.
// Workflow Angular DevTools + Cursor
// 1. Apri Angular DevTools (F12 > Angular tab)
// 2. Profila la change detection: ogni barra rossa = frame lento
// 3. Clicca sul componente lento - vedi le proprietà e il tempo CD
// 4. In Cursor, porta il contesto:
// "Angular DevTools mostra che ProductListComponent impega 450ms
// per la change detection. Il profiler indica che il re-render
// avviene 47 volte in 1 secondo durante lo scroll.
// @product-list.component.ts analizza e ottimizza la change
// detection strategy e l'uso degli input."
// Cursor risponderà con:
// - Analisi del componente
// - Identificazione dei problemi (mancanza di OnPush, trackBy, ecc.)
// - Implementazione ottimizzata
Debugging Tradizionale vs AI-Assisted: Quando Usare Cosa
L'AI non sostituisce tutti gli approcci di debugging. Ci sono scenari in cui il debugging
tradizionale manuale e ancora superiore o complementare.
Guida: Quale Approccio Usare
Scenario
Approccio Consigliato
Tool
Errore TypeScript / compilazione
Cmd+K inline
Cursor inline edit
Stack trace con errore runtime
Chat con contesto file
Cursor Chat + @file
Bug intermittente / race condition
Debug Mode
Cursor Debug Mode (2.2)
Memory leak progressivo
Profiler + AI analysis
Chrome DevTools + Cursor Chat
Performance regression
Profiler + AI optimization
Angular DevTools + Cursor
Bug in codice legacy sconosciuto
Debug Mode + @codebase
Cursor Debug Mode + contesto
Debugging hardware / low-level
Debugger nativo
gdb/lldb, breakpoints manuali
Security vulnerability
Code review manuale
Cursor + revisione umana esperta
Conclusioni: Costruire una Cultura del Debugging AI-Assisted
Il debugging con Cursor AI non e semplicemente una questione di strumenti, e un cambio
di mentalita. I developer più produttivi non "chiedono all'AI di fixare i bug" - costruiscono
un dialogo strutturato con l'AI, fornendo contesto preciso, verificando le ipotesi con test,
e usando gli strumenti giusti per ogni tipo di problema.
Debug Mode di Cursor 2.2 rappresenta un salto qualitativo: non e più necessario essere
esperti del codebase per fare debugging efficace. L'approccio hypothesis-driven con
strumentazione runtime riduce drasticamente il tempo speso in congetture. Bugbot sposta
parte del carico di rilevamento bug al momento della code review, riducendo il numero
di bug che arrivano in produzione.
Per i team Angular, il guadagno e particolarmente significativo nelle aree storicamente
più difficili: subscription leaks, race conditions negli Observable, flaky tests async
e performance della change detection. Cursor conosce questi pattern e li riconosce
rapidamente, dove un developer meno esperto potrebbe impiegare ore.
Checklist per Iniziare il Debugging AI-Assisted
Configura launch.json con source maps abilitate
Abilita sourceMap: true nel tsconfig.json
Crea le Cursor Rules per gli standard di debugging del tuo progetto
Pratica il template "BUG REPORT" nei prompt di debugging
Sperimenta Debug Mode su un bug reale nella tua codebase
Configura Bugbot se usi pull requests (GitHub/GitLab)
Integra il workflow: reproduce - test - fix - verify con Cursor
Continua la Serie Cursor IDE
Hai completato l'articolo sul debugging. Continua con gli altri articoli della serie: