🕹️ Aggiornamento 2026: questa guida è di gennaio 2025 e parla di Claude Sonnet 3.5 usato in chat. Le basi valgono ancora (contesto chiaro, richieste piccole, rileggere sempre il codice generato), ma oggi si lavora soprattutto con agenti come Claude Code, che leggono e modificano direttamente il progetto. Per il modo di lavorare attuale leggi come scrivere il prompt perfetto per un coding agent, i subagent di Claude Code per parallelizzare lo sviluppo e ContextDoc, la documentazione pensata per gli agenti.
Dopo aver esplorato le tecniche avanzate di pair programming, vediamo come sfruttare Claude per ottimizzare le performance del nostro codice. A differenza degli strumenti tradizionali di profiling, Claude può suggerire ottimizzazioni contestuali basate su pattern ricorrenti e best practice.
Analisi delle Performance
Ecco un dialogo reale con Claude per l’ottimizzazione di un endpoint REST:
Dev: Il mio endpoint /users sta diventando lento con l'aumentare degli utenti. Ecco il codice:
@Get('/users')
async getUsers(@Query() filters: UserFilters) {
const users = await this.userRepository.find({
where: filters,
relations: ['posts', 'comments', 'profile']
});
return users.map(user => ({
...user,
postCount: user.posts.length,
commentCount: user.comments.length
}));
}
Claude: Il codice presenta diverse aree di ottimizzazione:
1. State caricando tutte le relazioni anche quando servono solo i conteggi. Possiamo usare subquery per i conteggi:
@Get('/users')
async getUsers(@Query() filters: UserFilters) {
const users = await this.userRepository
.createQueryBuilder('user')
.select([
'user',
'(SELECT COUNT(*) FROM posts WHERE posts.userId = user.id) as postCount',
'(SELECT COUNT(*) FROM comments WHERE comments.userId = user.id) as commentCount'
])
.where(filters)
.getMany();
return users;
}
Questo riduce significativamente il carico di dati e migliora le performance. Vuoi che aggiunga anche paginazione e caching?
Dev: Sì, mostrami come implementare entrambi
Claude: Ecco la versione ottimizzata con paginazione e caching:
@Get('/users')
@CacheKey('users-list')
@CacheTTL(300) // 5 minuti
async getUsers(
@Query() filters: UserFilters,
@Query('page') page = 1,
@Query('limit') limit = 10
) {
const cacheKey = `users-${page}-${limit}-${JSON.stringify(filters)}`;
const cached = await this.cacheManager.get(cacheKey);
if (cached) {
return cached;
}
const [users, total] = await this.userRepository
.createQueryBuilder('user')
.select([
'user',
'(SELECT COUNT(*) FROM posts WHERE posts.userId = user.id) as postCount',
'(SELECT COUNT(*) FROM comments WHERE comments.userId = user.id) as commentCount'
])
.where(filters)
.skip((page - 1) * limit)
.take(limit)
.getManyAndCount();
const result = {
users,
meta: {
total,
page,
limit,
pages: Math.ceil(total / limit)
}
};
await this.cacheManager.set(cacheKey, result);
return result;
}
Dev: Come gestisco l'invalidazione della cache quando un utente viene modificato?
Claude: Ecco come gestire l'invalidazione della cache in modo intelligente:
@EventSubscriber()
class UserCacheInvalidator {
@OnEvent('user.updated')
@OnEvent('user.created')
@OnEvent('user.deleted')
async invalidateCache(payload: UserEvent) {
// Invalida solo le chiavi correlate all'utente specifico
const keys = await this.cacheManager.store.keys('users-*');
const userKeys = keys.filter(key =>
key.includes(`userId-${payload.userId}`)
);
await Promise.all(
userKeys.map(key => this.cacheManager.del(key))
);
// Invalida anche la lista principale
await this.cacheManager.del('users-list');
}
}
Memory Leaks e Debugging
Claude eccelle nell’identificare potenziali memory leaks. Ecco un esempio:
Dev: Ho un memory leak in questa WebSocket service:
@WebSocketGateway()
export class NotificationGateway {
private clients = new Map();
handleConnection(client: Socket) {
const userId = client.handshake.query.userId;
this.clients.set(userId, client);
}
handleDisconnect(client: Socket) {
// TODO: cleanup
}
}
Claude: Il problema è che non state rimuovendo i client disconnessi dalla Map. Ecco la versione corretta:
@WebSocketGateway()
export class NotificationGateway {
private clients = new Map();
handleConnection(client: Socket) {
const userId = client.handshake.query.userId;
this.clients.set(userId, client);
// Cleanup automatico
client.on('disconnect', () => {
this.clients.delete(userId);
});
}
// Aggiungi monitoraggio
@Interval(60000) // ogni minuto
async cleanupStaleConnections() {
for (const [userId, client] of this.clients.entries()) {
if (!client.connected) {
this.clients.delete(userId);
}
}
}
}
Aggiungi anche logging per tracciare le connessioni attive:
@Inject(Logger)
private logger: Logger;
handleConnection() {
// ...
this.logger.debug(
`Active connections: ${this.clients.size}`,
'NotificationGateway'
);
}
Query Optimization
Claude può analizzare piani di esecuzione delle query e suggerire ottimizzazioni:
Dev: Questa query sta diventando lenta:
SELECT p.*,
c.name as category_name,
COUNT(r.id) as review_count,
AVG(r.rating) as avg_rating
FROM products p
LEFT JOIN categories c ON c.id = p.category_id
LEFT JOIN reviews r ON r.product_id = p.id
WHERE p.price > 100
GROUP BY p.id, c.name
ORDER BY avg_rating DESC;
Claude: La query può essere ottimizzata in diversi modi:
1. Utilizzo di indici compositi:
CREATE INDEX idx_product_price ON products(price) INCLUDE (category_id);
CREATE INDEX idx_reviews_product ON reviews(product_id) INCLUDE (rating);
2. Query ottimizzata:
WITH product_reviews AS (
SELECT product_id,
COUNT(*) as review_count,
AVG(rating) as avg_rating
FROM reviews
GROUP BY product_id
)
SELECT p.*,
c.name as category_name,
COALESCE(pr.review_count, 0) as review_count,
pr.avg_rating
FROM products p
LEFT JOIN categories c ON c.id = p.category_id
LEFT JOIN product_reviews pr ON pr.product_id = p.id
WHERE p.price > 100
ORDER BY pr.avg_rating DESC NULLS LAST;
La CTE pre-aggrega i dati delle recensioni, riducendo il numero di join necessari. NULLS LAST migliora l'ordinamento per prodotti senza recensioni.
Dev: Posso vedere il piano di esecuzione prima e dopo?
Claude: Ecco l'analisi dei piani di esecuzione:
Query originale:
- Seq Scan su reviews
- Hash Join con products
- Sort per avg_rating
- Group By con sort
Costo stimato: 1245.67
Query ottimizzata:
- Index Scan su products (usando idx_product_price)
- Index Scan su reviews (usando idx_reviews_product)
- Hash Join con CTE
- Sort solo sul risultato finale
Costo stimato: 456.89
L'ottimizzazione riduce il costo del 63%. Vuoi che analizziamo anche i tempi di risposta reali?
Conclusione
L’ottimizzazione con Claude richiede un dialogo iterativo. Fornisci sempre metriche concrete e casi d’uso reali per ottenere suggerimenti più pertinenti. Nel prossimo articolo vedremo come utilizzare Claude nello sviluppo web.
