Claude Code สำหรับความล้มเหลวของ CI: พรอมต์สำหรับการแก้เล็กน้อย + การทดสอบ
Claude Code สำหรับข้อผิดพลาด CI: ให้พรอมต์ให้อ้างบรรทัดที่ล้ม แนะนำการแก้ที่เล็กที่สุด และเพิ่ม regression test เพื่อป้องกันไม่ให้เกิดซ้ำ

ปัญหาเมื่อ CI ล้มและ AI เดาไปไกล
การล้มของ CI มักไม่ใช่เรื่องลึกลับ Log จะบอกว่าหยุดที่ไหน คำสั่งไหนล้ม และข้อความผิดพลาดคืออะไร การรันที่มีประโยชน์มักมี stack trace, ข้อผิดพลาดจากคอมไพเลอร์พร้อมไฟล์และเลขบรรทัด หรือรายงานเทสต์ที่บอกว่า assertion ไหนล้ม บางครั้งคุณยังได้เบาะแสแบบ diff เช่น "expected X, got Y" หรือขั้นตอนที่ล้มชัดเจนเช่น "lint", "build" หรือ "migrate database".
ปัญหาจริงคือคน (และ AI) มักมอง log เป็นเสียงพื้นหลัง หากคุณแปะ log ยาว ๆ แล้วขอ "การแก้" โมเดลหลายตัวจะไปหาคำอธิบายที่คุ้นเคยแทนการอ่านบรรทัดสุดท้ายที่มีความหมาย การเดาจะยิ่งแย่เมื่อข้อผิดพลาดดูธรรมดา ("module not found", "timeout", "permission denied") ผลคือ rewrite ใหญ่, เพิ่ม dependency ใหม่ หรือคำตอบแบบ "ลองอัปเดตทุกอย่าง" ที่ไม่ตรงกับความล้มเหลวจริง
เป้าหมายไม่ใช่ "ทำให้มันผ่านยังไงก็ได้" แต่ง่ายกว่า:
- อ่านผลที่ล้ม
- ระบุการเปลี่ยนแปลงที่เล็กที่สุดที่จะทำให้ขั้นตอนที่ล้มผ่าน
- รักษาทุกอย่างที่เหลือไว้เหมือนเดิม
ในทางปฏิบัติ "การแก้ที่เล็กที่สุด" มักเป็นหนึ่งในสิ่งต่อไปนี้: การเปลี่ยนโค้ดไม่กี่บรรทัดในจุดเดียว, import หายหรือพาธผิด, ค่าคอนฟิกที่ผิดชัดเจนสำหรับสภาพแวดล้อม CI, หรือการย้อนการเปลี่ยนที่เผลอทำให้แตก แทนการออกแบบใหม่
การเพิ่มเทสต์ติดตามผลก็สำคัญเช่นกัน การที่ CI ผ่านครั้งหนึ่งไม่เท่ากับการป้องกันการเกิดซ้ำ ถ้าความล้มเกิดจากกรณีพิเศษ (input เป็น null, timezone, การปัดเศษ, สิทธิ์) ให้เพิ่ม regression test ที่ล้มก่อนแก้และผ่านหลังแก้ จะเปลี่ยนการช่วยเหลือครั้งเดียวให้กลายเป็นรั้วป้องกัน
ควรรวบรวมอะไรบ้างก่อนขอความช่วยเหลือ
การแก้ที่แย่มักเริ่มจากบริบทที่ขาดหาย ถ้าคุณแปะแค่บรรทัดแดงสุดท้าย โมเดลต้องเดาว่าเกิดอะไรขึ้นก่อนหน้า และการเดามักกลายเป็นการ rewrite
พยายามให้รายละเอียดพอที่คนจะตามความล้มจากข้อผิดพลาดแรกถึงตอนจบ แล้วเปลี่ยนให้น้อยที่สุด
คัดลอกสิ่งเหล่านี้ลงในข้อความของคุณ (คำพูดตามตัวเมื่อทำได้):
- Log ที่ล้มทั้งหมดตั้งแต่บรรทัดข้อผิดพลาดแรกจนจบ (ไม่ใช่แค่ stack trace สุดท้าย)
- คำสั่งที่ CI รันเป๊ะ ๆ (เช่น
go test ./...,npm test,flutter test,golangci-lint run) - พาธไฟล์ที่อ้างถึงใน error และคอนฟิกที่เกี่ยวข้อง (test config, linter config, build scripts)
- อะไรเปลี่ยนไปเมื่อเร็ว ๆ นี้: สรุป diff ของ PR, การ bump dependency, แก้ CI config
- มัน flaky ไหม: ถ้ามีการรันสองสามครั้งล้มและหนึ่งครั้งผ่าน ให้บอก
เพิ่มข้อจำกัดด้วยคำง่าย ๆ ถ้าคุณต้องการการแก้เล็ก ๆ ให้บอก: ห้าม refactor, ห้ามเปลี่ยนพฤติกรรมถ้าไม่จำเป็น, จำกัดแพตช์เฉพาะจุดที่ล้ม
ตัวอย่างง่าย: CI ล้มที่ขั้น lint หลังจาก bump dependency แปะ lint output เริ่มจากคำเตือนแรก ใส่คำสั่งที่ CI ใช้ และบอกการเปลี่ยนเวอร์ชันของแพ็กเกจเพียงรายการเดียว พอให้แนะนำการปรับคอนฟิกบรรทัดเดียวหรือการเปลี่ยนโค้ดเล็ก ๆ แทนการปรับฟอร์แมตทั้งรีโพ
ถ้าคุณต้องการสิ่งที่คัดลอกไปแปะได้ โครงสร้างนี้มักเพียงพอ:
CI command:
Failing output (full):
Recent changes:
Constraints (smallest fix, no refactor):
Flaky? (runs attached):
พรอมต์กฎที่บังคับให้มันอ่านข้อความที่ล้ม
เมื่อโมเดลพลาดเป้าบนการแตกของ CI มักเป็นเพราะพรอมต์ให้มันเดา หน้าที่คุณคือทำให้มันแสดงกระบวนการโดยใช้ผลลัพธ์ที่ล้มจริง ๆ แล้วยึดการเปลี่ยนแปลงที่เล็กที่สุดที่ทำให้งานผ่าน
กฎที่ทำให้โมเดลตรงไปตรงมา
บังคับหลักฐานและแผนเล็ก ๆ พรอมต์ที่ดีบังคับห้าข้อ:
- อ้างบรรทัดที่ล้มจาก CI log (errors, stack trace, file:line) และระบุว่า "ฉันกำลังใช้บรรทัดเหล่านี้"
- ให้การวินิจฉัยเป็นประโยคเดียว ไม่มีการอ่อนๆ
- เสนอแผนแพตช์เล็กที่สุด 1–3 แก้ไข โดยระบุไฟล์ที่จะแตะจริง ๆ
- ห้ามเปลี่ยนที่ไม่เกี่ยวข้อง (ห้ามฟอร์แมต, rename, refactor, bump dependency) เว้นแต่คุณอนุญาต
- ระบุสิ่งที่ไม่แน่ใจและข้อมูลเดียวที่จะยืนยันการวินิจฉัย
ความไม่แน่นอนเป็นเรื่องปกติ แต่การไม่เปิดเผยความไม่แน่นอนคือสิ่งที่เสียเวลา
เฟรกเมนต์พรอมต์พร้อมวาง
วางส่วนนี้ไว้บนสุดของคำถาม CI ของคุณ:
Use ONLY the evidence in the CI output below.
1) Quote the exact failing lines you are using.
2) Give ONE sentence: the most likely cause.
3) Propose the smallest fix: 1-3 edits, with file paths.
4) Do NOT do formatting/renames/refactors or "cleanup".
5) List uncertainties + the one extra detail that would confirm the diagnosis.
ถ้า log บอก "expected 200, got 500" พร้อม stack trace ไปที่ user_service.go:142 โครงสร้างนี้จะผลักให้คำตอบไปที่ฟังก์ชันนั้นและแก้เล็ก ๆ เช่น guard หรือจัดการ error แทนการออกแบบใหม่ของ endpoint
เท็มเพลตพรอมต์คัดลอก-วางสำหรับความล้มเหลว CI
พรอมต์ที่เร็วที่สุดคือพรอมต์ที่บังคับให้อ้าง log อยู่ในขอบเขต และหยุดเมื่อขาดข้อมูล
You are helping me fix a CI failure.
Repo context (short):
- Language/framework:
- Test/build command that failed: <PASTE THE EXACT COMMAND>
- CI environment (OS, Node/Go/Python versions, etc.):
Failing output (verbatim, include the first error and 20 lines above it):
<PASTE LOG>
Constraints:
- Propose the smallest possible code change that makes CI pass.
- Do NOT rewrite/refactor unrelated code.
- Do NOT touch files you do not need for the fix.
- If behavior changes, make it explicit and justify why it is correct.
Stop rule (no guessing):
- If the log is incomplete or you need more info (missing stack trace, config, versions, failing test name), STOP and ask only the minimum questions needed.
Your response format (follow exactly):
1) Evidence: Quote the exact log lines that matter.
2) Hypothesis: Explain the most likely cause in 2-4 sentences.
3) Smallest fix: Describe the minimal change and why it addresses the evidence.
4) Patch: Provide a unified diff.
5) Follow-up: Tell me the exact command(s) to rerun locally to confirm.
Then, write ONE regression test (or tweak an existing one) that would fail before this fix and pass after it, to prevent the same failure class.
- Keep the test focused. No broad test suites.
- If a test is not feasible, explain why and propose the next-best guardrail (lint rule, type check, assertion).
สองรายละเอียดที่ลดการคุยซ้ำ:
- ใส่คำสั่งที่ล้มและข้อผิดพลาดแรกให้ชัดเจน (ไม่ใช่แค่สรุปสุดท้าย)
- ถ้ามีหลายความล้ม ให้บอกว่าจะแก้อันไหนก่อน (ปกติคือความล้มครั้งแรกใน log)
วิธีผลักให้ได้การแก้เล็ก ๆ ไม่ใช่การเขียนใหม่หมด
วิธีที่เร็วที่สุดที่จะเสียเวลา คือยอมรับการเปลี่ยนแปลงแบบ "cleanup" ที่แก้หลายจุดพร้อมกัน นิยาม "เล็กที่สุด" ล่วงหน้า: แพตช์เล็กที่สุดที่ทำให้งานที่ล้มผ่าน และมีความเสี่ยงต่ำสุดและตรวจสอบเร็วที่สุด
กฎง่าย ๆ ที่ใช้ได้: แก้ symptom ก่อน แล้วค่อยพิจารณา refactor ถ้าจำเป็น ถ้า log ชี้ไปที่ไฟล์เดียว ฟังก์ชันเดียว import หาย หรือกรณีพิเศษ ให้มุ่งไปที่จุดนั้น หลีกเลี่ยงการแก้แบบ "ในเมื่อทำแล้ว"
ถ้าต้องการทางเลือก ให้ขอแค่สองแบบ: "แก้ที่ปลอดภัยที่สุดแบบ minimal" vs "แก้ที่เร็วที่สุดแบบ minimal" คุณต้องการ tradeoff ไม่ใช่เมนู
ยังบังคับการตรวจสอบท้องถิ่นให้ตรงกับ CI: ขอคำสั่งเดียวกับที่ pipeline รัน (หรือเทียบเท่าที่ใกล้เคียง) เพื่อยืนยันภายในไม่กี่นาที:
# run the same unit test target CI runs
make test
# or the exact script used in CI
npm test
ถ้าคำตอบเสนอการเปลี่ยนใหญ่ ให้ตอบกลับว่า: "แสดงแพตช์เล็กที่สุดที่แก้ assertion ที่ล้ม โดยไม่เปลี่ยนฟอร์แมตหรือชื่อไฟล์ที่ไม่เกี่ยวข้อง"
การพรอมต์สำหรับเทสต์ติดตามผลที่ป้องกันการเกิดซ้ำ
การแก้โดยไม่มีเทสต์คือการเดิมพันว่าจะไม่เจอปัญหาเดิมอีกเสมอ ขอเทสต์ติดตามผลเสมอที่ล้มก่อนแพตช์และผ่านหลังแพตช์
เจาะจงว่า "ดี" คืออะไร:
- ถ้าล้มเพราะ unit test crash ให้เขียนเทสต์ใหม่หรือเสริม assertion ที่เข้มขึ้น
- ถ้าล้มเพราะ build, lint หรือ formatting ให้เพิ่มเช็คที่บังคับกฎนั้นไว้
รูปแบบที่มีประโยชน์: ระบุให้ครบ 4 อย่าง: ที่วางเทสต์, ชื่อไฟล์เทสต์, พฤติกรรมที่ครอบคลุม, และบันทึกสั้น ๆ ว่าทำไมเทสต์นี้ป้องกันการเกิดซ้ำ
คัดลอกเพื่อเพิ่มเติม:
- เขียน regression test หนึ่งรายการที่ล้มบน branch main ปัจจุบันและผ่านหลังแก้
- ให้มันครอบคลุมคลาสของความล้ม ไม่ใช่แค่บรรทัดเดียวที่แตก
- วางเทสต์ใน: <path or folder> ตั้งชื่อตาม convention ของคุณ
- ถ้าเป็นกฎ lint/build ให้เพิ่มหรือตั้งกฎที่บังคับมัน
- เพิ่ม 2–3 ประโยค: ทำไมเทสต์นี้จะจับบั๊กที่คล้ายกันได้ในอนาคต
ตัวอย่าง: CI แสดง panic เมื่อ handler API ได้ ID ว่าง อย่าขอแค่ "เทสต์บรรทัดนี้" ให้ขอเทสต์ที่ครอบ ID ไม่ถูกต้อง (ว่าง, whitespace, รูปแบบผิด) แพตช์เล็กที่สุดอาจเป็น guard clause ที่คืน 400 เทสต์ติดตามผลควรตรวจพฤติกรรมสำหรับ input ไม่ถูกต้องหลายแบบ เพื่อให้การ refactor parsing ในอนาคตทำให้ CI ล้มทันที
ถ้าโปรเจกต์มี convention ของเทสต์ ให้บอกมัน หากไม่มี ให้ขอให้ทำตามเทสต์ที่อยู่ใกล้เคียงในแพ็กเกจ/โฟลเดอร์เดียวกัน และให้เทสต์ใหม่เรียบง่ายอ่านง่าย
เวิร์กโฟลว์ขั้นตอนที่นำกลับมาใช้ซ้ำได้
1) ให้มันรับความล้มเดิมโดยตรง
แปะส่วนของ log ที่มีข้อผิดพลาดและ ~20–40 บรรทัดด้านบน พร้อมคำสั่งที่ CI รันและรายละเอียดสภาพแวดล้อมสำคัญ (OS, เวอร์ชัน runtime, flags สำคัญ)
แล้วขอให้มันสรุปเป็นภาษาเรียบง่ายและชี้บรรทัดใน output ที่พิสูจน์ ถ้ามันไม่สามารถอ้าง log ได้ แปลว่ามันยังไม่ได้อ่านจริง
2) บังคับให้แพตช์เล็กที่สุดก่อน
ขอการเปลี่ยนโค้ดที่เล็กที่สุดที่ทำให้คำสั่งล้มผ่าน ดันกลับทุกการ refactor ก่อนนำไปใช้ ให้มันระบุ:
- ไฟล์ที่จะถูกแตะ
- พฤติกรรมที่เปลี่ยนแปลงอย่างชัดเจน
- สิ่งที่จะไม่เปลี่ยน
3) รันคำสั่งเดียวกันอีกครั้ง รักษา loop ให้แคบ
ใช้แพตช์และรันคำสั่งที่ล้มแบบเดียวกับ CI ในเครื่องของคุณ (หรือในงาน CI เดิมถ้านั่นเป็นทางเดียว) ถ้ายังล้ม ให้แปะ output ใหม่และทำซ้ำ การรักษาขอบเขตบริบทเล็กช่วยให้คำตอบโฟกัส
4) เพิ่ม regression test สำหรับคลาสของความล้ม
เมื่อผ่าน ให้เพิ่มเทสต์ติดตามผลหนึ่งรายการที่ล้มก่อนแพตช์และผ่านหลังแพตช์ จำกัดเทสต์: หนึ่งเทสต์, หนึ่งเหตุผล
รันคำสั่งอีกครั้งพร้อมเทสต์ใหม่เพื่อยืนยันว่าคุณไม่ได้แค่ปิดเสียง error
5) ปิด PR ด้วยชุดข้อมูลที่ชัดเจน
ขอข้อความ commit สั้น ๆ และคำอธิบาย PR ที่รวมว่าอะไรล้ม, อะไรเปลี่ยน, คุณยืนยันอย่างไร, และเทสต์อะไรที่ป้องกันการเกิดซ้ำ รีวิวจะเร็วขึ้นเมื่อเหตุผลชัดเจน
ตัวอย่างสมจริง: จาก log ที่ล้มถึงการแก้และเทสต์
ความล้มที่พบบ่อย: ทุกอย่างทำงานในเครื่อง แต่การเปลี่ยนเล็ก ๆ ทำให้เทสต์ล้มบน runner CI ตัวอย่างนี้มาจาก Go API ที่ handler เริ่มรับค่า date-only (2026-01-09) แต่โค้ดยัง parse เฉพาะ RFC3339 เต็มรูปแบบ
นี่คือตัวอย่างสั้น ๆ ที่ควรแปะ (เก็บให้สั้นแต่ใส่บรรทัด error):
--- FAIL: TestCreateInvoice_DueDate (0.01s)
invoice_test.go:48: expected 201, got 400
invoice_test.go:49: response: {"error":"invalid due_date: parsing time \"2026-01-09\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"\" as \"T\""}
FAIL
exit status 1
FAIL app/api 0.243s
ใช้พรอมต์ที่บังคับหลักฐานและแพตช์เล็ก ๆ ดังนี้:
You are fixing a CI failure. You MUST use the log to justify every claim.
Context:
- Language: Go
- Failing test: TestCreateInvoice_DueDate
- Log snippet:
<PASTE LOG>
Task:
1) Quote the exact failing line(s) from the log and explain the root cause in 1-2 sentences.
2) Propose the smallest possible code change (one function, one file) to accept both RFC3339 and YYYY-MM-DD.
3) Show the exact patch.
4) Add one regression test that fails before the fix and passes after.
Return your answer with headings: Evidence, Minimal Fix, Patch, Regression Test.
คำตอบที่ดีจะชี้ไปที่ mismatch ของ layout ที่ใช้ parse แล้วทำการเปลี่ยนเล็ก ๆ ในฟังก์ชันหนึ่ง (เช่น parseDueDate ใน invoice.go) ให้ลอง parse RFC3339 ก่อนแล้ว fallback ไปที่ 2006-01-02 ไม่ต้อง refactor หรือเพิ่มแพ็กเกจใหม่
เทสต์ติดตามคือรั้วป้องกัน: ส่ง due_date: "2026-01-09" และคาดว่า 201 หากใครต่อใคร later ลบ fallback การเปลี่ยน parsing CI จะล้มทันทีด้วย class เดิม
ความผิดพลาดทั่วไปที่เสียเวลา (และวิธีหลีกเลี่ยง)
วิธีที่เร็วที่สุดจะเสียชั่วโมงคือให้มุมมองปัญหาที่ครอบตัดเกินไป Log CI มีเสียงรบกวน แต่ส่วนที่มีประโยชน์มักอยู่ 20 บรรทัดขึ้นไปเหนือข้อผิดพลาดสุดท้าย
กับดักหนึ่งคือแปะแค่บรรทัดแดงสุดท้าย (เช่น "exit 1") ขณะที่สาเหตุจริงอยู่ก่อนหน้า (env var หาย เทสต์ snapshot ล้ม หรือเทสต์แรก crash) วิธีแก้: รวมคำสั่งที่ล้มและหน้าต่าง log ที่มีข้อผิดพลาดแรก
กับดักอื่นคือให้โมเดล "tidy up" ระหว่างทาง การเปลี่ยนฟอร์แมต, bump dependency, หรือ refactor ทำให้รีวิวยากและเสี่ยงที่จะทำอย่างอื่นพัง วิธีแก้: ล็อกขอบเขตเป็นการเปลี่ยนเล็กที่สุดและปฏิเสธสิ่งที่ไม่เกี่ยวข้อง
รูปแบบที่ควรระวัง:
- แปะแค่บรรทัดสุดท้าย: ให้รวมคำสั่งที่ล้มและข้อผิดพลาดแรก
- ให้มันแก้ dependencies หรือไฟล์ที่ไม่เกี่ยว: กำหนด minimal diff และเหตุผลสำหรับทุกไฟล์ที่แตะ
- ยอมรับการแก้ที่ไม่ได้ตรวจกับคำสั่ง CI: ให้รันคำสั่งเดียวกันเพื่อตรวจสอบ
- เขียนเทสต์ที่ยังผ่านเมื่อบั๊กกลับมา: ขอเทสต์ที่ล้มบนโค้ดเก่าและผ่านบนโค้ดแก้
- ผสม flaky กับ regression จริง: ตัดสินว่ามันเป็น nondeterministic หรือ logic ที่คงที่ แล้วจัดการตามนั้น
ถ้าคาดว่า flaky อย่าปิดด้วย retry แต่เอาความสุ่มออก (เวลา, RNG, temp dirs แยก) เพื่อให้สัญญาณชัด
ตรวจสอบด่วนก่อน push แพตช์
ก่อน push ให้ทำ sanity pass สั้น ๆ เป้าหมายคือยืนยันว่าสิ่งที่เปลี่ยนจริง เล็ก และทำซ้ำได้ ไม่ใช่แค่ผ่านด้วยโชค
- Evidence: คำอธิบายอ้างบรรทัดข้อผิดพลาดจริงหรือไม่?
- Scope: การเปลี่ยนจำกัดอยู่ในสิ่งที่จำเป็นหรือไม่?
- Causality: อธิบายได้ไหมว่าทำไมการเปลี่ยนนี้ทำให้ผ่าน?
- Repro: รันคำสั่ง CI เดิมหรือไม่ (flag, working dir เหมือนกัน)?
- Regression: เทสต์ใหม่ล้มก่อนแก้และผ่านหลังแก้หรือไม่?
สุดท้าย รันชุดคำสั่งที่กว้างขึ้นเล็กน้อยกว่างานที่ล้มเดิม (เช่น lint + unit tests) แพตช์ที่ผ่านงานเดิมแต่ทำให้เป้าหมายอื่นพังเป็นกับดักทั่วไป
ขั้นตอนถัดไป: ทำให้เวิร์กโฟลว์นี้เป็นนิสัย
ถ้าต้องการให้มันประหยัดเวลาเป็นสัปดาห์ต่อสัปดาห์ ให้ถือพรอมต์และโครงสร้างคำตอบเป็นกระบวนการทีม เป้าหมายคือลงอินพุตที่ทำซ้ำได้ ผลลัพธ์ที่ทำซ้ำได้ และข้อผิดพลาดน้อยลง
แปลงพรอมต์ที่ดีที่สุดของคุณเป็นสแนิปต์ในรีโพและปักหมุดในแชททีม กฎง่าย ๆ ที่ใช้ได้:
- เซฟพรอมต์เป็นสแนิปต์รีโพและปักไว้ในแชททีม
- แบ่ง CI failures ตามประเภท (lint, unit, integration, packaging, deploy)
- เมื่อป้ายซ้ำ ให้เพิ่มเทสต์หรือเช็คที่ควรจับมันก่อนหน้า
- เก็บการทดลองที่เสี่ยงให้ย้อนกลับได้ง่ายเพื่อถอยทันที
ถ้าคุณชอบเวิร์กโฟลว์แบบ chat-first ในการสร้างและวนซ้ำแอป คุณสามารถใช้ลูปแก้-ทดสอบเดียวกันใน Koder.ai ใช้สแนปชอตระหว่างทดลอง และส่งออกซอร์สเมื่อพร้อมผสานกลับเข้าผลิตจริง
คำถามที่พบบ่อย
ควรมองตรงไหนใน log ของ CI ก่อนเมื่อ job ล้มเหลว?
เริ่มจากข้อผิดพลาดตัวจริงตัวแรก ไม่ใช่ exit 1 สุดท้าย
- หาบรรทัดแรกที่แสดง อะไร ล้มเหลว (ชื่อเทสต์, file:line, คำสั่ง)
- อ่าน ~20–40 บรรทัดด้านบนเพื่อเข้าใจบริบท/การตั้งค่า
- อย่าไปสนใจข้อผิดพลาดที่ตามมาจนกว่าจะแก้ failure แรกให้ได้
ทำอย่างไรไม่ให้อะไหล่ AI เดาและให้การแก้ทั่วไป?
บอกให้มันพิสูจน์ว่ามันอ่านข้อความใน log จริง ๆ
ใช้ข้อจำกัดเช่น:
- “อ้างบรรทัดที่ล้มเหลวที่คุณใช้”
- “วินิจฉัยเป็นประโยคเดียว”
- “การแก้ที่เล็กที่สุด: 1–3 แก้ไขพร้อมไฟล์ที่แน่นอน”
- “หยุดแล้วถามถ้า log ไม่ครบ”
คำว่า “การแก้ที่เล็กที่สุด” จริง ๆ แล้วหมายถึงอะไรสำหรับความล้มเหลวของ CI?
โดยทั่วไปคือแพตช์ที่เล็กที่สุดซึ่งทำให้ขั้นตอนที่ล้มเหลวผ่าน
โดยมากหมายถึง:
- การเปลี่ยนโค้ดเป้าหมายหนึ่งจุด (เช่น guard clause, import ที่หายไป/พาธผิด)
- การแก้คอนฟิกเฉพาะ CI หนึ่งรายการ
- กลับการเปลี่ยนที่ทำให้แตกมากกว่าการออกแบบใหม่
หลีกเลี่ยงการ "ทำความสะอาด" จนกว่า CI จะผ่าน
ควรใส่อะไรบ้างเมื่อขอความช่วยเหลือเกี่ยวกับการรัน CI ที่ล้มเหลว?
ใส่บริบทพอให้สร้างซ้ำความล้มเหลวได้ ไม่ใช่แค่บรรทัดแดงสุดท้าย
รวม:
- คำสั่ง CI ที่แน่นอน (
go test ./...,npm test,flutter test, ฯลฯ) - log ตั้งแต่ บรรทัดข้อผิดพลาดแรก จนถึงตอนจบ
- พาธไฟล์ที่ถูกอ้างถึงใน error
- อะไรที่เปลี่ยนไปเมื่อเร็ว ๆ นี้ (dependency bump, แก้ CI config, สรุป PR)
- ถ้า flaky ให้ระบุผลรันที่ล้ม/ผ่าน
ฉันจะสั่งให้โมเดลไม่ทำ refactor หรือ reformat ได้ไหม?
ได้—ระบุข้อจำกัดด้วยภาษาตรงไปตรงมาและย้ำมัน
ตัวอย่างข้อจำกัด:
- “ห้าม refactor, rename, formatting หรือ bump dependencies”
- “แก้เฉพาะไฟล์ที่จำเป็นสำหรับการแก้”
- “ถ้าพฤติกรรมเปลี่ยน ให้บอกเปลี่ยนอะไรและเพราะเหตุใด”
ถ้า CI มีหลายข้อผิดพลาด ควรแก้อันไหนก่อน?
แก้ failure ที่เกิดก่อนเป็นอันดับแรก
- ข้อผิดพลาดที่ตามมามักเป็นผลมาจากตัวแรก (เช่น build ล้ม → tests/lint ไม่ถูกรันถูกต้อง)
- ถ้า error แยกกัน ให้เลือกอันที่บล็อกมากที่สุด (มักเป็น build/lint ก่อน integration)
ถ้าไม่แน่ใจ ให้ให้โมเดลระบุขั้นตอนแรกที่ล้มใน log แล้วยึดตามนั้น
จะรู้ได้อย่างไรว่า failure ใน CI เป็น flaky และควรทำอย่างไร?
ถือว่า flaky เป็นสัญญาณให้เอาความสุ่มออก ไม่ใช่เพิ่ม retry
ตัวช่วยปกติ:
- แช่เวลา (inject clock) หรือใช้ timestamp คงที่
- กำหนด seed ให้ RNG
- หลีกเลี่ยงการเรียกเครือข่าย (mock/stub)
- ใช้ temp dir แยก และพอร์ตเฉพาะ
เมื่อ deterministic แล้ว การแก้ที่เล็กที่สุดจะชัดเจนขึ้น
วิธียืนยันว่าแก้ไขตรงกับ CI และไม่ใช่การผ่านโดยบังเอิญคืออะไร?
ขอคำสั่งที่ CI รันเป๊ะ ๆ แล้วรันคำสั่งนั้นในเครื่องของคุณ
- ใช้คำสั่งและ flag เดียวกับ CI
- แมตช์เวอร์ชันสำคัญ (Go/Node/Flutter, OS)
ถ้า local reproduce ยาก ให้ขอ minimal repro ในรีโพ (เทสต์หรือ target เดียว) ที่กระตุ้นข้อผิดพลาดเดียวกัน
เทสต์ติดตามผลที่ดีหลังการแก้ CI ควรเป็นอย่างไร?
เขียนเทสต์ regression เดียวที่ล้มก่อนแพตช์และผ่านหลังแพตช์
เป้าหมายที่ดีได้แก่:
- กรณีขอบที่ทำให้ล้ม (null input, timezone, rounding, permissions)
- คลาสของความล้มเหลว (เช่น ID ที่ไม่ถูกต้องหลายรูปแบบ ไม่ใช่แค่เคสเดียว)
ถ้าเป็น lint/build ให้เพิ่มหรือตั้งกฎ lint ที่เข้มขึ้นเป็นรูปแบบ "เทสต์"
จะวนรันเร็วโดยไม่ทำให้รีโพรกเป็นขยะขณะดีบัก CI ได้อย่างไร?
ใช้ snapshot/rollback เพื่อทำให้การทดลองย้อนกลับได้
ลูปปฏิบัติได้จริง:
- ทำการเปลี่ยนแปลงที่เล็กที่สุด
- รันคำสั่งที่ล้มเดิม
- ถ้ายังล้ม ให้ย้อนกลับหรือใช้ snapshot แล้วลองแพตช์เล็กอื่น
หากใช้ Koder.ai สแนปชอตช่วยให้ทดลองเร็วโดยไม่ปนกับแพตช์สุดท้ายที่จะแปะกลับ