คุณสั่งให้ Claude Code แก้บั๊กหนึ่งตัว แล้วมอบฟีเจอร์เล็ก ๆ ให้ Codex จากนั้นก็ลุกไปทำอย่างอื่น พอกลับมาไฟล์ที่ได้กลับดูผิดปกติ การแก้ไขของเอเจนต์ตัวหนึ่งหายไป หรือไม่ก็มีการแก้ไขของทั้งสองตัวครบ แต่บิลด์กลับพัง หรือแย่กว่านั้นคือทุกอย่างคอมไพล์ผ่าน แต่มีเทสต์พังในจุดที่ไม่มีเอเจนต์ตัวไหนแตะเลย ระหว่างที่คุณไม่อยู่ ไม่มีอะไรแครชทั้งนั้น เอเจนต์ทั้งสองทำงานที่คุณสั่งครบถ้วน แต่ไฟล์เดียวไม่อาจรองรับผู้เขียนสองคนพร้อมกันได้ และไม่มีใครบอกคุณว่าตัวไหนชนะ
ความเงียบนั้นแหละคือปัญหาตัวจริง ไฟล์ไม่ใช่เครื่องมือ merge เมื่อเอเจนต์เขียนโค้ดสองตัวเขียนลง auth.ts ในไดเรกทอรีเดียวกันในช่วงเวลาที่ทับซ้อนกัน ไม่มีล็อก ไม่มีคิว ไม่มีคำเตือน มีเพียงโปรเซสที่เซฟทีหลังเท่านั้นที่ชนะ บทความนี้จะพาคุณไล่ดูการชนกันทีละขั้น ตั้งชื่อโหมดความล้มเหลวในแต่ละช่วง และปิดท้ายด้วยเช็กลิสต์ที่คุณรันได้ก่อนเริ่มเซสชันแบบขนานครั้งต่อไป
นอกจากนี้ยังชี้ให้เห็นถึงเวิร์กโฟลว์ที่วิธีแก้เหล่านั้นนำไปสู่ Sharkly คือแพลตฟอร์มสั่งการและจัดการเอเจนต์แบบครบวงจรที่วางตัวอยู่เหนือเอเจนต์เขียนโค้ดที่คุณใช้อยู่แล้ว ช่วยให้แต่ละ Task มี worktree แยกของตัวเอง พร้อมมองเห็นคอนเท็กซ์ ความคืบหน้า อุปสรรค ผลลัพธ์ และการรีวิวโดยมนุษย์ได้ตลอดตั้งแต่คำขอจนถึงการปล่อยจริง คุณสามารถสร้างวินัยแบบเดียวกันนี้ขึ้นเองได้ด้วย Git ธรรมดา และบทความนี้เกือบทั้งหมดจะแสดงวิธีให้ดู ส่วนตอนสุดท้ายจะพูดถึงว่าเมื่อไรที่การทำเองเริ่มไม่คุ้ม
ไม่มีล็อก: ใครเขียนทีหลังชนะ
เริ่มจากกรณีที่แย่ที่สุดก่อน เพราะมันพบบ่อยที่สุดและมองเห็นได้ยากที่สุด เอเจนต์ทั้งสองรันใน working directory เดียวกัน เอเจนต์ A อ่าน auth.ts วางแผนการแก้ไข แล้วเริ่มเขียน เอเจนต์ B อ่าน auth.ts ไฟล์เดียวกันในวินาทีถัดมา วางแผนการแก้ไขที่ต่างออกไปโดยอิงจากเวอร์ชันที่ตัวเองอ่าน แล้วก็เขียนลงไปเช่นกัน
ไม่มีใครอ่านการแก้ไขของอีกฝ่าย เพราะตอนที่อ่าน การแก้ไขนั้นยังไม่ถูกเซฟ ใครก็ตามที่เซฟทีหลังจะเขียนทับไฟล์บนดิสก์ด้วยเวอร์ชันที่ไม่เคยมีการทำงานของเอเจนต์ตัวแรกอยู่เลย นี่คือหลัก "ใครเขียนทีหลังชนะ" และมันเกิดขึ้นอย่างเงียบงัน ไม่มี conflict marker ไม่มี error ไม่มี exit code เอเจนต์ A รายงานว่าสำเร็จ เอเจนต์ B ก็รายงานว่าสำเร็จ หนึ่งในนั้นกำลังโกหกคุณโดยที่ตัวเองไม่รู้ตัว
การแย่งกันเขียน lockfile ก็คือความล้มเหลวแบบเดียวกันในจุดที่แพงกว่า เอเจนต์ A รัน npm install แล้วเขียน package-lock.json ใหม่ เอเจนต์ B ที่กำลังติดตั้งอยู่ก็เขียนมันใหม่อีกครั้งจากสถานะตั้งต้นที่ต่างกัน ผลลัพธ์คือ lockfile ที่ไม่ตรงกับ dependency tree ใดเลย และปัญหาจะโผล่บนเครื่องของคนอื่นในอีกหลายวันถัดมา shared build output, พอร์ต dev-server เดียว หรือแคชแบบ global ก็ล้มเหลวแบบเดียวกันหมด
วิธีแก้ไม่ใช่การทำให้เอเจนต์ฉลาดขึ้น แต่คือการให้เอเจนต์แต่ละตัวมีห้องของตัวเอง
หนึ่ง working copy ต่อเอเจนต์: การเขียนทับแบบเงียบกลายเป็นความขัดแย้งที่มองเห็นได้
Git worktree คือสำเนาที่เช็คเอาต์แล้วชุดที่สองของรีโพสิทอรีหนึ่ง ๆ อยู่บนแบรนช์ของตัวเอง โดยใช้ .git object store ร่วมกัน เอเจนต์แต่ละตัวจะได้ไดเรกทอรีจริงไว้แก้ไข บิลด์ และทดสอบได้ โดยไม่เห็นงานที่อีกฝ่ายกำลังทำอยู่
# From the main repo
git worktree add ../repo-agent-a -b agent/auth-refactor
git worktree add ../repo-agent-b -b agent/session-timeout
ชี้เอเจนต์ A ไปที่ ../repo-agent-a และเอเจนต์ B ไปที่ ../repo-agent-b ตอนนี้การแก้ไข auth.ts ที่ A ยังไม่เซฟจะมองไม่เห็นจาก B และไม่มีเอเจนต์ตัวไหนเขียนทับไฟล์ของอีกฝ่ายบนดิสก์ได้ เพราะทั้งคู่กำลังแก้ไฟล์คนละไฟล์บนคนละแบรนช์ โมเดล worktree ถูกอธิบายไว้ลึกกว่านี้ใน Git worktrees for AI coding agents และ coding agent worktrees: one checkout per task
สังเกตว่าการแยกส่วนทำอะไรตรงนี้ มันไม่ได้ป้องกันไม่ให้เอเจนต์สองตัวแก้ไฟล์เดียวกัน แต่เปลี่ยนการสูญเสียข้อมูลแบบเงียบ ๆ ให้กลายเป็น merge conflict ที่มองเห็นได้ในจังหวะที่แบรนช์กลับมาเจอกัน นั่นคือการยกระดับที่แท้จริง Git จะหยุดและแสดงบรรทัดที่ขัดแย้งกันอย่างชัดเจน แล้วคุณแก้ไขโดยเห็นทั้งสองเวอร์ชันเต็มตา conflict ที่คุณมองเห็นได้คือปัญหาที่คุณแก้ได้ ส่วนการเขียนทับที่คุณมองไม่เห็นนั่นแหละคือสิ่งที่ถูกปล่อยออกไป
มีข้อจำกัดหนึ่งที่ควรพูดให้ชัด: worktree แยกไฟล์ ไม่ได้แยก side effect ฐานข้อมูลที่ใช้ร่วมกัน พอร์ตเดียว หรือ path เอาต์พุตที่ฮาร์ดโค้ดไว้ ยังคงชนกันข้าม worktree อยู่ดี ต้องแยกสิ่งเหล่านั้นด้วย หรือไม่ก็จัดคิวให้เอเจนต์ที่ต้องใช้มันรันทีละตัว
ความขัดแย้งที่คุณมองไม่เห็น
นี่คือโหมดความล้มเหลวที่รอดพ้นจากการแยกส่วนมาได้ เอเจนต์ A ที่ทำงานใน worktree ของตัวเอง เปลี่ยนชื่อ getUser() เป็น fetchUser() และอัปเดตทุกจุดที่เรียกใช้ซึ่งอยู่ในความรับผิดชอบของตัวเอง เอเจนต์ B ที่ทำงานใน worktree ของตัวเองในช่วงเวลาเดียวกัน เขียนจุดเรียกใช้ getUser() ใหม่สามจุด เพราะนั่นคือชื่อฟังก์ชันตอนที่ B อ่านไฟล์
ทั้งสองแบรนช์ merge กันได้โดยไม่มี conflict marker เลย Git เปรียบเทียบบรรทัด และเมื่อดูทีละบรรทัดก็ไม่มีอะไรทับซ้อนกัน A แก้ไฟล์ของตัวเอง B เพิ่มไฟล์ใหม่ การ merge สะอาดหมดจด จากนั้นบิลด์ก็พังเพราะเจอ getUser ที่ไม่ถูกนิยาม หรือไม่ก็คอมไพล์ผ่านแต่ไปพังตอนรันไทม์ในจุดที่ type checker ไปไม่ถึง ไม่มีเครื่องมือไหนเตือน เพราะในเชิงข้อความมันไม่มีการชนกัน การแก้ไขทั้งสองครั้งขัดกันที่ความหมายของไฟล์ ไม่ใช่ตัวข้อความ
นี่คือเหตุผลที่ "แค่แยกเอเจนต์ออกจากกัน" ไม่ใช่คำตอบทั้งหมด การแยกส่วนเพียงเลื่อนการชนกันแบบไฟล์เดียวกันไปเป็นการชนกันแบบอินเทอร์เฟซเดียวกัน และแบบหลังซ่อนตัวได้เก่งกว่าแบบแรก คันโยกตรงนี้คือการกำหนดขอบเขตงาน ไม่ใช่เครื่องมือ กฎที่มีประโยชน์ข้อหนึ่ง: ถ้าคุณอธิบายงานของเอเจนต์สองตัวโดยไม่เอ่ยถึงไฟล์เดียวกันหรือฟังก์ชันเดียวกันไม่ได้ แสดงว่านั่นคืองานเดียว ไม่ใช่สองงาน การแบ่งงานตามขอบเขตโมดูลคือสิ่งที่ทำให้ Claude Code and Codex working together on one codebase ไม่กลายเป็นบ่ายที่ต้องมานั่งดีบั๊กเสียเป็นส่วนใหญ่ เมื่อสองงานใช้インターフェースร่วมกันจริง ๆ ให้จัดคิวทำทีละตัว หรือ merge และ rebase ระหว่างขั้นตอน เพื่อให้เอเจนต์ตัวที่สองได้อ่านสถานะจริงของเอเจนต์ตัวแรก
จุดที่การตั้งค่าแบบ DIY เริ่มขยายไม่ไหว และ task board เข้ามาเติมอะไร
เอเจนต์สองตัวกับไฟล์ที่ใช้ร่วมกันหนึ่งไฟล์ คุณจัดการด้วยมือได้: สร้างสอง worktree กำหนดขอบเขตอย่างระมัดระวัง แล้วคอยดูการ merge วิธีนี้ใช้ได้จนกว่าการนับจะเริ่มยากขึ้น เอเจนต์ตัวไหนอยู่บนแบรนช์ไหน worktree ไหนตรงกับ task ไหน การ merge สะอาด ๆ ห้าครั้งครั้งไหนที่แอบเปลี่ยนอินเทอร์เฟซร่วม แบรนช์ไหนรอรีวิวจากคุณ และอันไหนที่คุณดูไปแล้ว คำสั่ง Git ไม่เคยยากขึ้น แต่การจดบันทึกรอบ ๆ มันต่างหากที่ยากขึ้น และการจดบันทึกนั้นแหละคือจุดที่การชนกันแบบไฟล์เดียวกันหลุดรอดไปได้ เพราะคุณอนุมัติ merge ที่คุณไม่ได้อ่านครบ
task board เปลี่ยนสิ่งที่คุณกำลังติดตาม จากเดิมที่ต้องรับมือกับกำแพงของเทอร์มินัลและ worktree ที่แยกออกมา แต่ละหน่วยงานจะกลายเป็น Task ที่มีเจ้าของ มีแบรนช์ และมีสถานะ ใน Sharkly Task จะได้ worktree ที่แยกออกมาให้อัตโนมัติ การแยกส่วนระดับไฟล์จึงเป็นค่าเริ่มต้น แทนที่จะเป็นคำสั่งที่คุณต้องนึกให้ได้รัน การทำงาน อุปสรรค และผลลัพธ์จะกลับเข้ามาอยู่ใน Task คุณจึงรีวิวการเปลี่ยนแปลงเทียบกับขอบเขตที่ระบุไว้ แทนที่จะต้องย้อนเดาเจตนาจาก diff และการแบ่งบทบาทยังคงเดิม: เอเจนต์ค้นคว้า ลงมือทำ ทดสอบ และรายงาน ส่วนคนเป็นผู้กำหนดทิศทาง ให้อำนาจ และรับผลลัพธ์ ระบบอัตโนมัติหยุดตรงจุดที่ต้องใช้วิจารณญาณของทีม
ประเด็นสุดท้ายนี่แหละคือสิ่งที่การแยกส่วนเพียงอย่างเดียวไม่มีวันแก้ได้ เครื่องมือที่ดีขึ้นทำให้การแยกส่วนและการ merge ง่ายขึ้น แต่การรีวิวไม่ได้ขยายตามจำนวนเอเจนต์ มันขยายตามความสนใจของคุณ และการรีวิวโดยมนุษย์คือคอขวดตัวจริงในงานเอเจนต์แบบขนาน เอเจนต์ห้าตัวสร้างห้าแบรนช์ในเวลาที่คุณอ่านได้อย่างละเอียดแค่หนึ่งอัน task board ไม่ได้อ่านโค้ดแทนคุณ แต่มันทำให้คิวซื่อสัตย์ เพื่อให้การแก้ไขที่ดูสมเหตุสมผลไม่ถูกกวาดตาแล้ว merge ไปเพราะคุณจำไม่ได้ว่ามันอยู่ในกองไหน คณิตศาสตร์ของความขัดแย้งแบบเดียวกันนี้ยังเกิดในระดับรีโพด้วย ดูได้ใน multiple coding agents in the same repository
คุณจะสร้างสิ่งนี้ด้วย worktree และวินัยก็ได้ หรือจะใช้ Sharkly จัดการเวิร์กโฟลว์เลยก็ได้ ถ้าคุณรันเอเจนต์หลายตัวอยู่แล้ว คุณค่าคือการมีที่เดียวที่งานและผลลัพธ์ของพวกมันอยู่รวมกัน แทนที่จะกระจัดกระจายไปตามเทอร์มินัลต่าง ๆ
เช็กลิสต์ที่ควรรันก่อนเริ่มเซสชันแบบขนานครั้งต่อไป
รันสิ่งนี้ก่อนที่คุณจะชี้เอเจนต์สองตัวไปที่อะไรก็ตาม:
- ให้เอเจนต์แต่ละตัวมี worktree ของตัวเอง อย่าให้เอเจนต์สองตัวอยู่ใน working directory เดียวกันเด็ดขาด ใช้
git worktree add ../repo-<task> -b agent/<task>ต่อหนึ่ง task ดู parallel agents in isolated worktrees - กำหนดขอบเขตงานไม่ให้เอ่ยถึงไฟล์หรือฟังก์ชันเดียวกัน ถ้าจำเป็นต้องเอ่ย นั่นคืองานเดียว ให้จัดคิวทำทีละตัว
- แยก side effect ไม่ใช่แค่ไฟล์ แยกฐานข้อมูล พอร์ต path เอาต์พุต และแคชออกจากกัน หรือจัดคิวให้เอเจนต์ที่ใช้ร่วมกันรันทีละตัว
- Rebase ก่อน merge เมื่อสองแบรนช์เริ่มจากคอมมิตตั้งต้นเดียวกัน เพื่อให้งานของเอเจนต์ตัวที่สองวางทับอยู่บนงานของตัวแรก
- รัน type check และชุดเทสต์เต็มทุกครั้งหลัง merge ไม่ใช่รอทำทีเดียวหลังอันสุดท้าย ความขัดแย้งเชิงความหมายผ่านการ merge สะอาด ๆ มาได้และจะไปพังตรงนี้
- รีวิวแต่ละแบรนช์เทียบกับขอบเขต task ของมัน ทีละอัน กองเครื่องหมายถูกสีเขียวที่ยังไม่ได้อ่านนั่นแหละคือที่ที่ความขัดแย้งล่องหนถูกปล่อยออกไป
เอเจนต์สองตัวแก้ไฟล์เดียวไม่ใช่เคสขอบที่พบยาก แต่คือหน้าตาของงานแบบขนานในวินาทีที่งานของคุณเหลื่อมกันแม้เพียงนิดเดียว จงวางแผนรับมือการชนกันทำให้มันมองเห็นได้ และให้มีคนเพียงคนเดียวตัดสินว่าอะไรจะถูก merge



