Salta al contenuto principale

Wave-based parallel execution: speedup 3-5x con sub-agent paralleli

Cluster 10 della serie Claude Code Workflow. Le wave di GSD permettono parallelism vero su fasi multi-plan: plan della stessa wave con file disjoint vengono eseguiti contemporaneamente. Speedup misurato 3-5x. Pattern, isolation, anti-pattern.

Cosa ne pensi di questo articolo?

Wave-based parallel execution: speedup 3-5x con sub-agent paralleli
Claude Code WorkflowFoto:PixabayPexels

In Breve

Cluster 10 della serie Claude Code Workflow. Le wave di GSD permettono parallelism vero su fasi multi-plan: plan della stessa wave con file disjoint vengono eseguiti contemporaneamente. Speedup misurato 3-5x. Pattern, isolation, anti-pattern.

Risposta diretta

La condizione essenziale per il parallelismo è che i piani paralleli toccino file disgiunti, ovvero non modifichino gli stessi file. Se ci sono modifiche condivise in una wave dedicata, si utilizza un pattern di aggregazione dei frontmatter in un passaggio sequenziale.

Eseguire dieci plan in sequenza richiede tempo lineare. Eseguire dieci plan in tre-quattro wave parallele dimezza (o più) il wall-clock. Le wave di GSD non sono ottimizzazione opzionale: sono il meccanismo principale di scaling quando le fasi crescono in dimensione. Le ho misurate sul portfolio: 3-5x speedup tipico, su fasi tipo Phase 20 hanno fatto la differenza fra "eseguibile in un giorno" e "spalmato su una settimana".

Disclaimer di indipendenza: non sono affiliato a qrx né ad Anthropic. Le misurazioni di speedup sono sul mio campione personale, 19 fasi GSD eseguite, ~80 plan totali distribuiti in ~30 wave.

Cosa è una wave

Una wave è un gruppo di plan che possono essere eseguiti in parallelo senza interferenze. Ogni NN-NN-PLAN.md ha un campo wave: nel frontmatter (es. wave: 2). Plan con stessa wave + file_modified disjoint = parallelizzabili.

Il workflow /gsd-execute-phase fa:

  1. Legge tutti i PLAN.md della fase.
  2. Raggruppa per wave.
  3. Per ogni wave (in ordine), rispetta depends_on dei plan dentro la wave.
  4. Spawn N executor sub-agent in parallelo (uno per plan).
  5. Attende che tutti completino la wave.
  6. Verifica successo + commit batch (o gestione fallimenti).
  7. Procede alla wave successiva.

Esempio reale: Phase 20 wave structure

Phase 20 (la fase che ha pianificato questa serie editoriale) ha 10 plan distribuiti in 5 wave:

Wave 1 (sequential, CHECKPOINT):
  20-01 - infra scaffold + PII audit + OSS skeleton

Wave 2 (parallel x2):
  20-02 - backend lead-magnet PDF stack
  20-03 - frontend 9 nuovi component + SeoService

Wave 3 (parallel x3):
  20-04 - cornerstone /career/claude-code-workflow/
  20-05 - sub-page /career/superpowers/
  20-06 - sub-page /career/gsd/

Wave 4a (single):
  20-07A - build pipeline Mermaid + highlight + OSS full

Wave 4b (parallel x2):
  20-07B - cluster 01-09 IT body 6000 parole
  20-07C - cluster 10-18 IT body + internal linking

Wave 5 (sequential, CHECKPOINT):
  20-08 - i18n bulk + Lighthouse + verify + go-live

Wave 2 e Wave 3 sono dove il parallelism conta di più: 5 plan paralleli su 10 = 50% della fase eseguito in metà del tempo lineare.

Speedup misurato

Sul portfolio, distribuzione speedup wall-clock per type di wave:

  • Wave 1 plan singolo: 1x (sequenziale per definizione).
  • Wave 2-plan paralleli: 1.7-1.9x (overhead orchestration + token sharing).
  • Wave 3-plan paralleli: 2.5-2.8x.
  • Wave 4-plan paralleli: 3.0-3.5x (cap effettivo intorno a 3-4 plan, oltre il quale orchestration overhead degrada speedup).

Nota: speedup wall-clock; total token usage resta lineare in N plan (nessun risparmio nel pagare per i sub-agent). Il vantaggio è il tempo, non i token.

La condizione essenziale: file disjoint

Parallelism funziona solo se i plan paralleli toccano file disjoint. Se Plan A e Plan B nella stessa wave modificano entrambi blog-manifest.json, c'è race condition: l'ultimo a scrivere vince, le modifiche del primo vengono perse.

Plan-checker (cluster 9207) ha questo controllo nelle sue dimensioni: verifica che files_modified di plan paralleli abbiano intersection vuota. Se trova overlap, BLOCKER richiede o:

  1. Spostare un plan in wave diversa (sequenzializzazione).
  2. Riassegnare il file shared a un plan singolo "owner".
  3. Splittare il file shared in file per-plan-disjoint.

Pattern: shared file in wave dedicata

Quando un file deve essere modificato da molti plan, pattern usato sul portfolio:

  1. Wave N: plan paralleli che producono output da aggregare.
  2. Wave N+1: plan singolo che aggrega output nel file shared (es. manifest, registry).

Esempio dalla Phase 20: i 18 cluster article in Wave 4b producono frontend/public/content/articles/<id>/it.json per ogni cluster (file per-cluster disjoint). Wave 5 ha un plan che aggrega tutti i frontmatter in blog-manifest.json + _series_registry.json in singolo step sequenziale.

Anti-pattern: parallelism con dipendenza non dichiarata

Esempio realistico: Plan A scrive uno script migrate.sh; Plan B (stessa wave) modifica package.json per chiamare migrate.sh in postinstall. Plan A e B non modificano gli stessi file (disjoint apparente), ma Plan B dipende semanticamente da Plan A (deve esistere lo script prima di referenziarlo).

Soluzione: dichiarare depends_on: [planA] nel frontmatter di Plan B. Wave-based parallelism poi gestisce: anche se entrambi sono in Wave 2, B aspetta che A completi prima di iniziare.

Pattern: split per dominio

Quando una fase è naturalmente split-friendly per dominio (backend separato da frontend separato da i18n), la wave si organizza in modo ovvio:

  • Plan backend: solo file backend/**.
  • Plan frontend: solo file frontend/src/** + frontend/public/**.
  • Plan i18n: solo file frontend/src/locale/**.
  • Plan content: solo file frontend/public/content/articles/**.

Disjoint per costruzione. Speedup massimo, zero coordination overhead. Pattern preferito quando applicabile.

Pattern: split per feature

Quando il dominio è single layer ma la fase ha più feature indipendenti, split per feature:

  • Plan feature A: frontend/src/app/pages/feature-a/**.
  • Plan feature B: frontend/src/app/pages/feature-b/**.
  • Plan feature C: frontend/src/app/pages/feature-c/**.

Tre feature frontend in parallel, ognuna isolata nella sua cartella. Wave 3 di Phase 20 segue questo pattern: cornerstone + sub-page SP + sub-page GSD = tre plan paralleli, tre cartelle disjoint.

Esecuzione parziale: --wave N

Per pacing, quota management, o staged rollout, GSD permette di eseguire una sola wave:

/gsd-execute-phase 20 --wave 2

Esegue solo i plan di Wave 2. Verification e completion phase happen solo quando no incomplete plans rimangono dopo la wave selezionata. Utile per:

  • Dividere una fase mostro in sessioni multiple (Phase 20 ha 5 wave; eseguire 1 per giorno).
  • Pausare per checkpoint umano fra wave (es. Wave 1 Phase 20 richiede approval Buttondown list + OSS repo Federico).
  • Gestire quota Anthropic (sub-agent rate limit) distribuendo nel tempo.

Output marker e completion detection

Ogni executor sub-agent restituisce marker che orchestrator parsa:

  • ## TASKS COMPLETE: plan terminato con successo.
  • ## TASKS COMPLETE - CHECKPOINT REQUIRED: plan terminato, ma serve azione umana prima di procedere (autonomous: false).
  • ## EXECUTION BLOCKED: plan fermato per blocker (es. test rosso non risolvibile, dipendenza mancante non documentata).

Orchestrator parsa marker, decide se procedere a wave successiva o fermarsi per checkpoint/blocker. Senza marker convenzionali parsing è fragile.

Sessione tipica: Phase 20 wave 2-3 wall-clock

Misurazione approssimativa Phase 20:

  • Wave 1 (1 plan, sequential): ~2 ore wall-clock.
  • Wave 2 (2 plan parallel, backend ∥ frontend): ~3 ore wall-clock (vs ~5-6 sequential).
  • Wave 3 (3 plan parallel): ~3 ore wall-clock (vs ~7-8 sequential).
  • Wave 4a (1 plan): ~2 ore wall-clock.
  • Wave 4b (2 plan parallel): ~4 ore wall-clock (vs ~7-8 sequential).
  • Wave 5 (1 plan, sequential): ~3 ore wall-clock.

Totale wall-clock: ~17 ore. Sequenziale stimato: ~25-30 ore. Speedup effettivo: ~1.7-1.8x (limitato dai plan singoli sequenziali in Wave 1 + 4a + 5).

Gestione fallimenti durante wave

Cosa succede se uno dei N plan in una wave parallela fallisce mentre gli altri girano? Comportamento GSD:

  1. I plan in corso completano (no kill cascading).
  2. Orchestrator aggrega risultati: chi è OK, chi è BLOCKED.
  3. Se almeno 1 BLOCKED: stop wave successiva.
  4. Chiedi decisione: fix-blocked (interventi manuali), re-spawn-blocked (re-spawn solo il plan failed), abort-phase.

Plan completati in wave parziale restano committati (atomic per plan). Re-spawn fixed riprendere dal plan failed senza ri-eseguire i success.

Anti-pattern: troppe wave

Splittare ossessivamente in wave piccole (8 wave per 10 plan, ognuna con 1-2 plan) elimina parallelism: ogni wave attende la successiva, perdi il vantaggio.

Bilancio: 3-5 plan per wave è il sweet spot. Sotto 3 = poca parallelizzazione. Sopra 5 = orchestration overhead (notifiche async, verify status, coordination cost).

Anti-pattern: parallelism con file lock fisico

Anche se file_modified sono disjoint logicamente, alcuni file hanno lock fisico runtime (es. node_modules/.package-lock.json, frontend/dist/** durante build). Plan paralleli che triggerano npm install o ng build contemporaneamente possono corromperli a vicenda.

Mitigation: plan che eseguono comandi locking-prone vanno serializzati (wave separata o depends_on esplicito).

Checklist setup wave parallele

  1. Plan paralleli hanno file_modified disjoint? (verifica con plan-checker)
  2. Plan paralleli non triggerano comandi locking-prone (npm install, build)? Se sì, serializza.
  3. Dipendenze semantiche (anche con file disjoint) dichiarate in depends_on?
  4. Output marker convention rispettata (## TASKS COMPLETE)?
  5. File shared (manifest, registry) gestiti in wave dedicata sequenziale?

Cosa ho imparato scrivendo questo articolo

La lezione più importante è sulla natura del parallelism nel brownfield. Su greenfield (vertical slice MVP) è difficile parallelizzare perché tutto è connesso. Sul brownfield maturo, la maggior parte dei plan in una fase tematica orizzontale sono naturalmente indipendenti. Non li paralelizzi per ottimizzazione, li paralelizzi perché già lo erano. Wave structure rende visibile questa indipendenza.

Secondo insight: cap effettivo del parallelism è 3-4 plan per wave. Oltre, orchestration overhead (notifiche, verify status, fallimenti parziali da gestire) consuma il guadagno. 8 plan paralleli in una wave singola sembra ideale; in pratica è gestione caotica. Meglio splittare in due wave da 4.

Anti-pattern evitati

  • Wave con file overlap nascosto. Sempre verifica con plan-checker che files_modified siano disjoint. Race condition silenziosa è la peggior categoria di bug.
  • Troppe wave. 3-5 plan per wave. Meno = sotto-utilizzo parallelism; più = orchestration overhead.

Una storia ricevuta che vale la pena condividere

Sezione segnaposto. Se hai setup wave parallele con N>5 plan che hanno funzionato (o che si sono auto-distrutte), scrivimi a federico@federicocalo.dev.


← Cluster precedente: Agent orchestration Prossimo cluster: Ingest-docs synthesizer →

Domande frequenti

Cosa è una wave in GSD?

Una wave è un gruppo di plan che possono essere eseguiti in parallelo senza interferenze.

Qual è la condizione essenziale per il parallelismo funzionare in GSD?

La condizione essenziale è che i plan paralleli toccino file disjoint, ovvero non modificano gli stessi file.

Come si gestiscono le dipendenze non dichiarate tra plan nella stessa wave?

Le dipendenze non dichiarate possono essere gestite dichiarando la dipendenza nel frontmatter del plan che dipende da un altro, in modo che il parallelismo possa aspettare che il primo plan completi prima di iniziare.

Qual è il bilancio ottimale per il numero di plan per wave?

Il bilancio ottimale è di 3-5 plan per wave, al di sotto di cui si perde parallelizzazione e al di sopra del quale si accumulano overheads di orchestrazione.

Fonti e approfondimenti

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti