Agentic Workshop
Modulo 03 · 60 min

Esplorare, correggere, rifattorizzare e usare Git

Qui Task Notes diventa una piccola codebase. Imparerai a orientarti senza leggere tutto, a distinguere una correzione da un refactoring e a usare Git come strumento di comprensione e recupero.

Lezioni

Cosa affronti, passo dopo passo

3.1

Costruire una mappa prima di modificare

Davanti a una codebase nuova, leggere tutto è quasi sempre il modo più lento per capire. Ti serve una mappa del percorso rilevante: dove entra il dato, dove viene trasformato, dove viene conservato e dove appare.

Esplorare non significa aprire ogni file. Parti dalla struttura, individua l’entry point, segui i dati dal confine di ingresso fino all’output e trova i test che descrivono il comportamento. Questa mappa riduce il contesto e limita le supposizioni.

Chiedi all’agente di citare file e simboli che sostengono la sua spiegazione. Se il bug appare nell’interfaccia ma nasce nel parser, modificare la vista nasconde il sintomo senza correggere la causa.

Per capirlo nel concreto · Seguire una priorità dal CSV allo schermo

Nel CSV la priorità è `alta`, ma Task Notes la mostra come `normale`.

  1. Parti dal comando di importazione e individui `import_csv()` come ingresso, senza aprire ancora l’intero progetto.
  2. Segui il valore in `parser.py`: la colonna viene letta correttamente e passata al validatore.
  3. Nel validatore trovi una lista che accetta soltanto valori inglesi; `alta` cade quindi sul valore predefinito prima di raggiungere lo store.
  4. Confronti i test e la documentazione del formato per capire se il contratto prevede italiano, inglese o entrambi prima di proporre la patch.

Risultato: La vista non viene toccata: hai localizzato il primo punto in cui il dato cambia e hai anche identificato la decisione di prodotto da chiarire.

3.2

Dal sintomo all’ipotesi

Il sintomo è ciò che osservi; l’ipotesi è la tua spiegazione provvisoria. Tenerli separati è fondamentale: puoi essere certo del sintomo e avere comunque torto sulla causa.

Una buona riproduzione contiene input, azione e risultato osservato. Trasformala in un test fallente prima della patch quando è pratico: il test impedisce alla soluzione di inseguire un’idea astratta e diventa una protezione futura.

Formula una sola ipotesi primaria e descrivi cosa dovrebbe osservare il test se fosse vera. Se l’esperimento la smentisce, aggiorna la mappa; non allargare subito la modifica.

  1. Riproduci.
  2. Localizza il confine responsabile.
  3. Scrivi l’ipotesi.
  4. Fai fallire un test mirato.
  5. Applica la patch minima e riesegui.
Per capirlo nel concreto · Due attività uguali dopo un doppio clic

Un utente riferisce che, facendo doppio clic su «Salva», a volte compaiono due attività identiche.

  1. Descrivi il caso minimo: titolo `Pagare bolletta`, due clic rapidi, due record osservati invece di uno.
  2. Formuli l’ipotesi che il pulsante resti attivo mentre la prima richiesta è in corso; prevedi quindi due richieste quasi simultanee nel log.
  3. Il test di interazione riproduce i due clic e fallisce mostrando due chiamate. A questo punto non stai più indovinando: il test mostra il comportamento che avevi previsto.
  4. Disabiliti il pulsante durante il salvataggio, riesegui lo stesso test e controlli anche che un errore riabiliti il controllo per un nuovo tentativo. Se un duplicato avrebbe conseguenze serie, aggiungi anche una protezione sul server: la stessa richiesta non deve creare due record in caso di retry di rete.

Risultato: Il test protegge sia l’assenza del duplicato sia la possibilità di riprovare. La patch risponde al comportamento osservato, non a una congettura generica sulla rete.

3.3

Bug fix e refactoring non sono sinonimi

Una correzione e un refactoring possono toccare la stessa funzione, ma rispondono a domande diverse. La correzione cambia un comportamento sbagliato; il refactoring cambia la struttura senza cambiare il comportamento protetto.

Il bug fix cambia intenzionalmente un comportamento scorretto. Il refactoring migliora la struttura mantenendo il comportamento verificato. Separarli rende il diff leggibile e permette di attribuire un eventuale fallimento alla modifica giusta.

Se una funzione è difficile da correggere, applica prima la patch minima. Dopo che i test passano, valuta un secondo cambiamento strutturale con test invariati. Due commit raccontano meglio due intenzioni.

Per capirlo nel concreto · Correggere lo sconto, poi chiarire la formula

`totale(100, 20)` restituisce `99.8` perché la funzione sottrae `20 / 100` invece di applicare il 20 per cento al prezzo. Qui isoliamo la formula; in un’app reale, per il denaro useremmo centesimi interi o un tipo decimale adatto.

  1. Aggiungi il test `totale(100, 20) == 80` e lo esegui sulla versione corrente: fallisce con il valore osservato.
  2. Nel primo cambiamento sostituisci soltanto la formula con `prezzo * (1 - sconto / 100)` e riesegui i casi esistenti.
  3. Controlli e registri un diff minimo che contiene test e formula, senza spostare la funzione o rinominare l’intero modulo.
  4. In un secondo cambiamento introduci nomi come `percentuale_sconto` e separi la validazione, mantenendo invariati gli stessi risultati testati.

Risultato: Puoi rilasciare o annullare la correzione indipendentemente dalla pulizia strutturale, e ogni review ha una domanda precisa a cui rispondere.

3.4

Git come rete di sicurezza leggibile

Git è utile quando racconta una storia comprensibile: cosa c’era prima, cosa hai cambiato e perché. Non è una licenza per cancellare alla cieca, soprattutto quando nella stessa cartella esiste lavoro che non appartiene alla tua missione.

`status` mostra lo stato del lavoro, `diff` racconta la modifica e un commit atomico conserva un punto comprensibile. Prima di ripristinare qualcosa, verifica se esistono cambiamenti dell’utente che non appartengono al compito.

Preferisci operazioni recuperabili. Un revert aggiunge una correzione alla storia condivisa; riscrivere o cancellare la storia può essere appropriato solo in un contesto controllato e con autorità chiara.

Per capirlo nel concreto · Committare la patch senza prendere il lavoro altrui

Hai corretto `parser.py`, ma `git status` mostra anche modifiche in `theme.css` e un nuovo file `notes-private.txt` che non hai creato.

  1. Non ripristini né aggiungi tutto. Esamini il diff di `parser.py` e confermi che contiene soltanto la patch e il test concordati.
  2. Non sai da dove arrivano `theme.css` e il file non tracciato: li tratti come lavoro non tuo, li lasci intatti e segnali che erano già presenti.
  3. Prepari esplicitamente solo i file della correzione e controlli `git diff --staged` prima del commit.
  4. Esegui il test pertinente e crei un messaggio che descrive il comportamento corretto, non una formula vaga come «aggiornamenti vari».

Risultato: Il commit contiene una sola intenzione e il lavoro estraneo resta nel working tree. La rete di sicurezza non diventa una fonte di perdita dei dati.

Esempio guidato

Uno sconto applicato male

Il totale di un’attività a pagamento usa la percentuale come valore assoluto.

# Bug
def totale(prezzo, sconto):
    return prezzo - sconto / 100

# Patch minima
def totale(prezzo, sconto):
    return prezzo * (1 - sconto / 100)