Ottimizzazione del Codice con Claude Sonnet 3.5

Ottimizzazione del codice con Claude AI
Quando l'AI incontra l'ottimizzazione delle performance
Mauto 4 min

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.

content_copy Copiato