Il contratto REST che devi rispettare a ogni richiesta

L’API Claude è un servizio REST su https://api.anthropic.com, e per un’applicazione conversazionale quasi tutto passa da un endpoint solo, POST /v1/messages. Gli header obbligatori sono x-api-key (in alternativa Authorization con un token da Workload Identity Federation), anthropic-version e content-type. Con un SDK ufficiale non li scrivi tu: la libreria Python invia anthropic-version a 2023-06-01 per conto suo.

La parte che riguarda davvero chi progetta il client è la politica di versioning. Dentro una versione Anthropic preserva i parametri di input e di output esistenti, ma si riserva di aggiungere input opzionali, aggiungere valori all’output e aggiungere nuove varianti ai valori enum-like, per esempio nuovi tipi di evento di streaming. Tradotto in codice: il deserializzatore deve tollerare campi sconosciuti, e ogni ramificazione su stop_reason o sul tipo di evento SSE ha bisogno di un caso di default che non sollevi. È il modo tipico di rompersi in produzione senza aver toccato una riga.

Gli errori arrivano sempre come JSON con la stessa forma: un oggetto error di primo livello con type e message, più request_id.

{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "The requested resource could not be found."
  },
  "request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}

I codici da gestire per davvero sono 400 invalid_request_error, 401 authentication_error, 413 request_too_large (32 MB sui Messages), 429 rate_limit_error, 500 api_error e 529 overloaded_error. La regola la scrive la documentazione: «Catch the SDK’s typed classes rather than string-matching error messages, handling the most specific classes first.» E logga sempre l’header request-id: è l’unico appiglio quando apri un ticket.

Asincrono vuol dire tre cose diverse

Il primo livello è il client asincrono: AsyncAnthropic espone esattamente la stessa interfaccia di quello sincrono, e per concorrenza alta esiste l’extra anthropic[aiohttp]. Il secondo è lo streaming SSE, che serve quando la risposta è lunga: il timeout di default è dieci minuti, e l’SDK Python solleva un ValueError se stima che una richiesta non in streaming li supererà. Il ritentativo merita attenzione perché è silenzioso: «Certain errors are automatically retried 2 times by default, with a short exponential backoff», e fra gli errori ritentati c’è anche il timeout. Se la chiamata ha effetti collaterali a valle, li paghi due volte.

Il terzo livello è il Message Batches API, che non è una variante dello streaming ma un modello di esecuzione diverso: costa il 50% in meno, la maggior parte dei batch chiude entro un’ora, e il limite è «either 100,000 Message requests or 256 MB in size, whichever is reached first». I risultati si leggono da results_url quando processing_status passa da in_progress a ended. Qui c’è l’errore classico, e vale la pena riportarlo per esteso: «Batch results can be returned in any order, and may not match the ordering of requests when the batch was created.» Chi indicizza i risultati sulla posizione dell’array di input ottiene dati mescolati che sembrano plausibili. Si usa custom_id, sempre.

Il ciclo di sviluppo: dove Claude entra e dove no

Sul controllo di versione la scelta di fondo è che la configurazione dell’agente è codice: CLAUDE.md e .claude/settings.json si committano come qualsiasi altro file e si recensiscono in pull request come qualsiasi altra modifica.

Per la CI c’è la GitHub Action anthropics/claude-code-action@v1, con due modalità decise da un solo input: senza prompt risponde alla menzione @claude in issue e pull request, con prompt parte da sola su qualunque evento GitHub. Il segreto è ANTHROPIC_API_KEY oppure CLAUDE_CODE_OAUTH_TOKEN. Il tranello che costa mezza giornata è documentato: «GitHub doesn’t trigger workflows on commits made with the default GITHUB_TOKEN». Se passi quel token all’action, i commit di Claude non fanno partire la tua CI; la soluzione è non passarlo e lasciare che si autentichi come GitHub App.

Per lavorare in parallelo senza che due sessioni si pestino i file ci sono i worktree: claude --worktree feature-auth crea una directory isolata su un nuovo branch, e Claude Code blocca attivamente le scritture che tornerebbero nel checkout principale.

Refactoring: quello piccolo e quello che non entra in un prompt

Per il piccolo, /code-review esamina i commit del branch più le modifiche non committate; puoi passargli un target — un percorso, un numero di PR, un branch, un intervallo come main...my-feature — e i flag --fix e --comment. Gira come subagent in background con la sua context window, e questo ha una conseguenza pratica: le modifiche applicate da --fix stanno fuori dai checkpoint della sessione, quindi /rewind non le annulla e si torna indietro con git.

Distingui i due file di guida. CLAUDE.md è contesto di progetto e le violazioni nuove escono come nit; REVIEW.md è istruzione dedicata alla review, letta dagli agenti che trovano e verificano i rilievi, e non espande la sintassi di import.

Il refactoring su larga scala non si affronta con un prompt più lungo, ma restringendo cosa Claude vede: regole Read in permissions.deny per codice generato e vendored, un plugin di code intelligence al posto di grep per trovare definizioni e riferimenti, worktree.sparsePaths per checkout parziali. Sul metodo la documentazione è netta, e contraria all’istinto di spezzettare: «Give Claude the whole change in one session», perché passare la modifica condivisa e i suoi call site insieme mantiene coerenti le decisioni. E pianifica prima: il piano viene scritto su file e re-iniettato dopo ogni compattazione, mentre la cronologia della conversazione no.