Avem o aplicație simplă cu o listă de cumpărături și două lucruri de adăugat: filtrarea elementelor și exportul listei în CSV. Sarcinile au puține legături între ele, așa că ideea vine de la sine: împărțim lucrul la doi și terminăm ambele părți în jumătate de oră, nu într-o oră. Un agent preia filtrul, celălalt exportul; fiecare primește propriul folder de lucru, ca modificările să nu se amestece. N-au discutat însă dinainte cum reprezintă ambele părți un element din listă. Părea de la sine înțeles.

După jumătate de oră, ambele părți sunt gata. Testele fiecăreia trec, iar combinarea s-a făcut fără niciun conflict. Dar scenariul complet nu funcționează: filtrul afișează trei rânduri pe ecran, iar coloana din fișierul exportat e goală. Cauza e banală. Unul dintre agenți a redenumit din mers câmpul completed în done, fiindcă așa i s-a părut mai clar, iar celălalt n-a aflat.

Folderele separate au ținut fișierele la distanță, dar n-au aliniat deciziile. Să parcurgem problema în trei pași: mai întâi vedem dacă merită să împărțim lucrul, apoi distribuim zonele de modificare și convențiile comune, iar la final verificăm rezultatul asamblat, ca surpriza să nu apară abia la capăt.

Verificați dependențele

De obicei, există două motive pentru a împărți lucrul. Primul este cercetarea independentă a unor zone diferite: să aflăm în paralel cum se afișează lista și cum se generează exportul. Al doilea este lucrul simultan la părți ale căror limite și rezultate verificabile sunt deja cunoscute. Împărțirea în "plan, execuție, verificare" nu cere de la sine trei participanți; o singură sesiune poate face pașii pe rând. Pentru o sarcină mică, e rezonabil să începeți cu un singur agent.

Dacă există un motiv, următoarea întrebare este: ce parte din lucrul B poate continua cât timp rezultatul A nu e cunoscut? Dacă B depinde în întregime de A, pașii se fac pe rând. Uneori, B poate pregăti dinainte o parte independentă, dar combinarea rezultatelor trebuie totuși să aștepte după A.

Trei situații din același exemplu cu lista de cumpărături:

Ce trebuie făcutSe poate începe în paralelDe ce
Să găsim unde se filtrează lista acum și cum se generează exportuldasunt citite zone diferite, iar concluziile nu depind una de alta
Să pregătim remedierea finală, deși cauza încă nu e cunoscutădupă verificarea cauzeimai întâi cercetăm; ipoteze diferite și verificabile pot fi cercetate în paralel
Să scriem filtrul și exportuldupă stabilirea unui format comun pentru elementambele părți folosesc același câmp; fără o decizie comună, rezultatele nu se vor mai potrivi

Al treilea rând ascunde cea mai frecventă capcană. Pare că, dacă filtrul stă în src/filter.js, iar exportul în src/export-csv.js, părțile sunt independente: la urma urmei, fișierele sunt diferite. Dar ambele citesc același element și același câmp completed. Așa s-a stricat scenariul de la începutul articolului, iar fișierele diferite nu l-au salvat. Independența ține de comportament și convenții comune, nu de numărul de fișiere în care se află codul.

Puteți cere agentului să împartă lucrul, dar fără să pornească agenții:

Împarte sarcina în părți. Pentru fiecare, indică datele de intrare, rezultatul,
dependențele și metoda de verificare. Arată care părți pot începe acum.
Nu porni încă agenții.

Puteți verifica propunerea înainte de pornire. Părțile fără dependențe nerezolvate sunt candidate pentru lucru în paralel. Merită să verificați coloana goală a dependențelor în raport cu proiectul: agentul poate să nu fi observat un model sau o setare comună.

Calculați timpul până la rezultatul comun

De obicei, viteza se măsoară printr-un singur număr: cât a durat lucrul propriu-zis. Dar trebuie să măsurăm timpul până la rezultatul comun acceptat. Pe lângă lucru, acesta include definirea sarcinii pentru fiecare agent, recitirea proiectului de către fiecare, așteptarea, verificarea celor două rezultate și asamblarea lor.

Un calcul orientativ pentru exemplul nostru. Numerele sunt alese pentru aritmetică, nu reprezintă o măsurătoare de performanță:

Ce calculămUn agent, pe rândDoi agenți, în paralel
Pregătirea suplimentară a împărțirii: două definiții de sarcină și alinierea dintre agenți0 minute5 minute
Execuție20 + 20 de minute20 de minute pentru ambele părți
Verificarea și asamblarea rezultatelor10 minute15 minute
Total până la rezultatul acceptat50 de minute40 de minute
Total dacă o parte trebuie refăcută în 15 minute din cauza interpretării diferite a elementului50 de minute55 de minute

Pregătirea comună a sarcinii e necesară în ambele variante, așa că nu apare în calcul. O singură refacere consumă cele zece minute câștigate, iar cu cât asamblarea e mai costisitoare, cu atât mai mult timp trebuie să economisească lucrul în paralel.

Pe lângă timp, mai sunt două costuri ușor de uitat. Fiecare agent suplimentar recitește proiectul, ceea ce consumă resurse, iar rapoartele detaliate ocupă loc în conversația comună după ce revin. Al doilea cost e mai important: atenția dumneavoastră. Trebuie să citiți ambele rezultate, să le comparați și să decideți ce faceți cu ele. Dacă nu mai reușiți să citiți ce primiți, ritmul nu-l mai dictați dumneavoastră.

O accelerare de aproape două ori e posibilă pentru două părți independente și comparabile, cu puține costuri suplimentare. Dar simplul număr de agenți nu o garantează.

Dublură sau verificare

Dublarea lucrului nu se vede în rapoarte. Doi agenți deschid în liniște aceleași fișiere, fiecare încearcă să înțeleagă cum e reprezentat un element din listă și fiecare scrie sincer că a analizat codul. Dar uneori a doua trecere e intenționată: presupunerea e critică și vreți s-o verifice cineva care n-a văzut raționamentul primului agent. Diferența față de o dublură accidentală este că verificarea are dinainte un obiect, un motiv și un plan pentru situația în care răspunsurile diferă. Fără ultimul punct, verificarea devine doar o colecție de opinii.

Acum, situația inversă. Doi agenți au fost întrebați ce înseamnă completed într-un element al listei. Amândoi au răspuns la fel: arată că produsul a fost cumpărat. Pare o confirmare din două surse.

De fapt, amândoi au citit aceeași linie din README.md, scrisă acum un an, deși câmpul are de mult alt înțeles în cod. Nu concluzia lor a coincis, ci sursa.

De aici rezultă câteva obiceiuri utile:

  • cereți să vi se arate pe ce se bazează răspunsul: fișierul, rândul, fragmentul de cod. Nu o parafrază, ci ceva ce puteți deschide chiar dumneavoastră;
  • verificați separat și pe bune orice presupunere comună: ce face codul și ce se întâmplă în scenariu. Chiar dacă toată lumea e de acord.

Sesiunile separate nu garantează că erorile sunt independente: o sursă comună poate duce la aceeași greșeală. Iar când răspunsurile diferă, analizați dovezile, nu numărați voturile: unul poate consulta codul actual, în timp ce doi se bazează pe o notă învechită.

Valoarea câmpului verificată în cod devine baza modelului comun, asupra căruia vă înțelegeți înainte de pornire. Dacă împărțirea încă pare avantajoasă după toate cele trei verificări, trecem la repartizare.

Numiți un responsabil pentru fiecare zonă

Regula pentru lucrul în paralel e simplă: fiecare zonă de modificări are un singur responsabil. Nu "la fișierul acesta nu lucrează niciodată doi deodată", ci "cât timp se desfășoară aceste două sarcini, aici scrie el". E o convenție temporară, nu o proprietate a proiectului.

A doua jumătate a regulii e mai importantă și se uită mai des. Și rezultatul comun are un responsabil: cel care va asambla părțile și va decide dacă totul e gata. Dacă nimeni nu e desemnat pentru asamblare, sarcina îi revine celui care termină ultimul, deși tocmai el știe cel mai puțin despre partea celuilalt.

Înainte de pornire, uitați-vă la proiect și verificați dacă părțile necesare sunt deja separate într-un fel. Dacă nu, nu începeți un refactoring mare doar de dragul lucrului în paralel: acela va deveni cea mai mare sarcină a zilei și nu va mai rămâne nimic de împărțit.

Pentru lista noastră de cumpărături, repartizarea arată astfel:

ZonăCine modificăCondiție
src/filter.js, tests/filter.test.jsAgentul ASelectează elementele după câmpul convenit completed
src/export-csv.js, tests/export-csv.test.jsAgentul BExportă setul primit în formatul convenit
src/item-model.jsResponsabilul de asamblareModelul comun al elementului e convenit înainte de pornire; modificările se discută cu toți cei afectați
src/app.js, tests/filter-export.test.jsResponsabilul de asamblareConectează ambele părți și verifică exportul listei filtrate
package.json, package-lock.jsonResponsabilul de asamblareAgenții nu adaugă pe parcurs dependențe noi

Tabelul repartizează responsabilitatea pentru modificări, nu drepturile de acces: toți cei care au nevoie pot citi fișierele comune.

Rândul despre package.json nu e acolo din răutate: npm install modifică ambele fișiere dintr-odată, așa că două instalări în paralel provoacă un conflict din senin, iar două versiuni diferite ale aceleiași biblioteci pot aduce surprize la asamblare. Dacă amândoi au nevoie de o bibliotecă, agenții explică ce le trebuie și de ce. Decizia o ia și modificarea o face o singură persoană, o singură dată.

Stabiliți modelul comun înainte de pornire

Limitele stabilite doar după fișiere nu funcționează prea bine fără o convenție comună. Filtrul și exportul folosesc același model de element. Mai jos e o schiță a interfețelor, nu un exemplu executabil: implementările funcțiilor nu sunt incluse. Înainte de pornire, am mai convenit să păstrăm ordinea elementelor, să nu modificăm tabloul original și să scriem valorile booleene în CSV ca true/false. Regulile de escapare CSV rămân neschimbate.

// un element din listă: aceste trei câmpuri sunt stabilite înainte de pornire și nu se redenumesc
const item = { id: 41, title: "lapte de ovăz", completed: false };

// filter.js returnează setul de elemente selectate, nu formatează și nu exportă nimic
filterItems(items, mode);   // mode: "all", "active", "completed"

// export-csv.js primește setul deja filtrat și nu selectează nimic
exportCsv(items);           // returnează un șir CSV cu coloanele id,title,completed

Observați că înțelegerea nu privește numele fișierelor, ci datele de intrare și de ieșire. "Exportul primește lista deja filtrată" pare un detaliu minor, dar tocmai această propoziție stabilește care dintre cei doi răspunde de selectarea elementelor. Fără ea, amândoi pot presupune în mod rezonabil că filtrarea le revine, iar fișierul va conține fie totul, fie elementele filtrate de două ori.

Înainte de pornire, stabiliți și cum veți accepta rezultatul. Scenariul comun pentru exemplul nostru: elementele 41 și 43 au completed: true, iar elementul 42 are false. Modul "completed" trebuie să afișeze 41 și 43 în ordinea inițială, iar CSV-ul trebuie să conțină aceleași două elemente, cu antetul id,title,completed și valoarea true în ultima coloană. Scenariul trece prin ambele părți și depistează exact problema de la începutul articolului.

Mai e ceva despre schimbarea convenției. Dacă pe parcurs devine clar că formatul comun trebuie modificat, nu e o mică ajustare într-un singur fișier. E un motiv să reevaluați toate sarcinile care depind de format, fiindcă o parte din lucrul deja făcut se poate baza pe versiunea veche.

Folderele de lucru separate (git worktree) nu înlocuiesc o înțelegere comună. Ele separă fizic modificările, ceea ce ajută: într-un folder comun, o verificare poate include modificări neterminate dintr-o sarcină vecină și poate valida o stare pe care niciun agent n-a verificat-o separat. Dar un folder nu poate stabili formatul elementului, iar porturile, bazele de date locale și alte resurse externe rămân comune. Un .env neurmărit implicit nici nu există în folderul nou: se copiază, de exemplu prin .worktreeinclude.

Dați fiecăruia o instrucțiune scurtă

Instrucțiunea nu trebuie să fie lungă. De obicei, ajung șapte puncte: obiectivul, versiunea de la care să pornească, fișierele proprii, convențiile comune, rezultatul verificabil, ce să returneze în răspuns și ce să facă dacă are nevoie de o zonă care aparține altcuiva.

Obiectiv: filtrul listei afișează toate elementele, doar pe cele nefinalizate sau doar pe cele finalizate.
Începeți de la: ultimul commit al branch-ului principal, în care s-a stabilit modelul elementului.
Fișierele dumneavoastră: src/filter.js și tests/filter.test.js.
Convenție comună: câmpul completed rămâne boolean, nu schimbăm numele câmpurilor;
filtrul returnează un tablou de elemente și nu exportă nimic.
Rezultat: testele filtrului trec; pentru o listă de trei elemente, fiecare mod
returnează lista așteptată.
Returnați: lista fișierelor modificate efectiv, rezultatul rulării testelor
și versiunea pe care au fost rulate, plus întrebările rămase.
Dacă trebuie să modificați src/item-model.js sau src/app.js: opriți-vă,
descrieți modificarea necesară și motivul, apoi așteptați răspunsul.

Instrucțiunea pentru al doilea agent e construită la fel: perechea lui de fișiere, același format al elementului, un rezultat verificabil propriu și aceeași procedură dacă trebuie să treacă de limite. Nu e nevoie să o rescriem integral aici.

Acum, despre răspuns. El trebuie să includă lista reală a fișierelor modificate și rezultatul real al verificărilor. Fraza "fișierele altora n-au fost atinse" e ușor de scris, iar agentul o va include cu atât mai ușor cu cât ați cerut-o mai insistent. Verificați lista modificărilor, nu formularea. Limita din instrucțiune e o convenție, nu o interdicție: textul cererii nu împiedică agentul să scrie într-un fișier; drepturile de acces se configurează separat.

Când o parte din lucru îi revine unui subagent

În Claude Code, un subagent lucrează în propria fereastră de context. Un subagent obișnuit nu vede conversația dumneavoastră și lucrează pe baza descrierii sarcinii scrise de Claude; varianta fork moștenește conversația integral. Așadar, includeți explicit în instrucțiune convențiile și criteriile comune. Subagentul va returna un rezumat: e ușor de citit, dar nu arată exact ce s-a schimbat în fișiere și ocupă la rândul lui spațiu în conversația principală. Mai multe detalii: documentația despre subagenți.

Renegociați zonele care se suprapun

Mai devreme sau mai târziu, cineva va ajunge la limita zonei altcuiva. Agentul B descoperă că nu are unde să conecteze exportul fără să modifice src/app.js: butonul de export nu e apelat.

Un răspuns bun include motivul, modificarea necesară și dependența. Conectarea în src/app.js îi revine deja responsabilului de asamblare, așa că B își poate termina modulul și poate cere conectarea. Dacă nu-și poate continua partea fără o modificare comună, lucrul dependent se suspendă.

Există două variante. Responsabilul de asamblare modifică el însuși fișierul comun, fiindcă îi revine acea zonă. Sau o atribuie explicit agentului B, dar mai întâi se asigură că proprietarul anterior nu mai lucrează la acel fișier: transferul fără oprire creează tocmai situația de care repartizarea încearcă să vă protejeze.

Ce nu merită să faceți este să-l puneți pe agent să ocolească limita. Cererea "nu atinge app.js, găsește altă soluție" duce de obicei la o copie a logicii altcuiva în propriul fișier sau la un strat suplimentar pe care nimeni nu-l mai poate explica. Formal, tabelul e respectat. în practică, proiectul ajunge să aibă două versiuni ale adevărului despre cum funcționează exportul.

Asamblați comportamentul, nu doar fișierele

Înainte de asamblare, responsabilul trebuie să știe trei lucruri: de la ce versiune a pornit fiecare parte, ce a verificat fiecare agent și pe ce versiune, precum și ce fișiere s-au modificat efectiv. Toate acestea trebuie să apară deja în răspunsurile la instrucțiuni, iar lista fișierelor se compară cu modificările reale.

Apoi părțile se combină, iar pasul esențial este rularea scenariului comun stabilit înainte de pornire. Două probleme foarte diferite pot sta în cale.

Conflict textual. Părțile au modificat diferit aceeași zonă, au șters sau redenumit un fișier, iar Git n-a putut să le combine automat. Rezolvați conflictul, apoi verificați comportamentul rezultat.

Incompatibilitate semantică. Nimeni n-a modificat același rând împreună cu altcineva, modificările se combină fără probleme și nu apare niciun mesaj de conflict. Aici intervine cazul nostru cu redenumirea: filtrul citește done, exportul citește completed și primește o valoare goală. Testele ambelor părți trec, pentru că fiecare își verifică propriul comportament pe propriile date.

O combinare fără conflicte nu înseamnă compatibilitate

O combinare fără conflicte nu dovedește că părțile se comportă compatibil. Puteți rula scenariul comun prin interfață sau cu un test care leagă funcțiile; testele locale nu-l înlocuiesc.

Cele două părți sunt asamblate împreună și verificate cu un scenariu comunAgentul AAgentul BResponsabilul de asamblareScenariu comunfiltrul și exportul împreunăcombină părțilesrc/filter.jssrc/export-csv.js
Un singur responsabil asamblează părțile, iar scenariul comun arată dacă sunt compatibile

În diagramă, priviți săgeata de jos. Partea de sus reprezintă ce se numește de obicei împărțirea lucrului: două părți independente, fiecare în fișierul ei. Concluzia că totul e gata apare un nivel mai jos, în scenariul care parcurge ambele părți împreună. Până nu treceți de acea săgeată, aveți două rezultate locale, dar niciun rezultat comun. Dacă regulile proiectului cer o suită completă de verificări, scenariul comun nu o înlocuiește.

Stabiliți decizia comună

După asamblare, responsabilul de rezultatul comun alege pasul următor. De exemplu:

  1. Acceptați rezultatul comun: scenariul trece, modificările sunt explicabile și criteriile sunt îndeplinite.
  2. Trimiteți o anumită parte la refacere și precizați ce anume nu se potrivește și cu ce. Nu "exportul e cam ciudat", ci "exportul citește un câmp care nu mai există în model".
  3. Reevaluați împărțirea: reduceți numărul de agenți sau faceți părțile pe rând.

A treia variantă e normală, nu un eșec. Semnele că a venit momentul să reevaluați echipa apar destul de repede:

  • formatul comun al elementului se schimbă iar și iar, iar de fiecare dată trebuie refăcute ambele părți;
  • agenții se așteaptă unul pe altul: înseamnă că exista o dependență, doar că nu fusese menționată;
  • rezultatele sosesc mai repede decât reușiți să le citiți.

Ultimul semn e cel mai neplăcut, fiindcă din exterior pare un succes. Iar o singură împărțire reușită nu e încă o regulă: rezultatul agentului se schimbă de la o rulare la alta, așa că merită să confirmați o concluzie importantă și pe o sarcină comparabilă.

Și un rezultat parțial e un rezultat normal. Filtrul poate fi gata și verificat, dar exportul nu, iar acest lucru trebuie consemnat așa cum e: una din două părți. Sarcina comună rămâne deschisă. Nu o puteți declara încheiată doar fiindcă jumătate funcționează; nicio repartizare atentă nu schimbă asta.

Verificați-vă

Două situații. Răspundeți mai întâi singuri, apoi verificați.

Cauza erorii încă nu e cunoscută.
Uneori, exportul produce un fișier gol și apare tentația să puneți imediat doi agenți să-l repare. Începeți prin reproducerea și investigarea problemei. Dacă găsiți ipoteze diferite care pot fi verificate, le puteți încredința celor doi agenți. Dar două remedieri propuse fără o cauză identificată nu rezolvă încă problema: fiecare trebuie legată de cauză și verificată în scenariul inițial.

Fișiere diferite, dar aceeași valoare.
Filtrul numără produsele în bucăți, iar exportul scrie aceeași cantitate în pachete. Testele ambelor părți trec, dar numerele de pe ecran și din fișier nu coincid. Nu e o eroare de cod: niciuna dintre părți nu și-a încălcat limitele. Lipsește o convenție privind unitatea de măsură. Aceasta se stabilește în modelul comun al elementului înainte de pornire, responsabilul de asamblare modifică src/item-model.js, apoi scenariul comun se rulează din nou.

Ce preia Claude Code

Așa cum am văzut la începutul articolului, agentul poate propune o schiță a împărțirii și, pe baza punctelor dumneavoastră, poate redacta o instrucțiune coerentă. Mai sunt trei locuri în care vă poate ajuta, dar decizia rămâne la dumneavoastră:

  • Schița zonelor de lucru. Cereți-i să parcurgă proiectul și să propună ce fișiere se modifică pentru fiecare parte a sarcinii, indicând căile. E doar o schiță: dumneavoastră numiți responsabilul pentru fiecare zonă.
  • Asamblarea și rularea comună. Agentul va combina părțile, va afișa lista completă a modificărilor și va rula scenariul comun. După aceea, examinați toate modificările chiar dumneavoastră.
  • Un punct disputat. Două explicații formulate cu încredere nu înseamnă că decizia e luată. Cereți dovezi, fișierul și rândul relevant; tabelul stabilește dinainte cine modifică fișierul comun, nu cât de convingător sună răspunsul cuiva.

În cele din urmă, totul depinde de o decizie și două desemnări. Decizia: merită să împărțiți lucrul? Fiecare agent suplimentar trebuie să aducă un beneficiu care justifică pregătirea, verificarea și asamblarea părții lui. Desemnările: câte un responsabil pentru fiecare zonă de modificări și unul pentru rezultatul comun. În exemplul cu lista de cumpărături au lipsit tocmai acestea: convenția despre câmpul completed și desemnarea celui care să testeze filtrul și exportul împreună. Instrumentele preiau partea mecanică a lucrului, dar un om decide dacă comportamentul rezultat este într-adevăr cel cerut.