- Spec-Driven Development (SDD) คือแนวทางในการเขียนโค้ดด้วย AI ที่ เขียนสเปกก่อน ลงมือเขียนโค้ด โดยให้สเปกทำหน้าที่เป็นแหล่งความจริงหลัก (source of truth) สำหรับทั้งนักพัฒนาและ AI
- SDD แบ่งออกเป็น 3 ระดับของการนำไปใช้ และพัฒนาเป็นลำดับจาก spec-first (เขียนสเปกก่อน), spec-anchored (เก็บรักษาสเปกไว้เพื่อการบำรุงรักษา), ไปจนถึง spec-as-source (ใช้สเปกเป็นไฟล์ต้นทางหลัก และนักพัฒนาไม่แก้โค้ดโดยตรง)
- Kiro มี เวิร์กโฟลว์ 3 ขั้นตอนที่เรียบง่าย คือ requirements → design → tasks, spec-kit ใช้เวิร์กโฟลว์ที่อิงกฎอย่างเข้มข้นผ่าน constitution และ Tessl กำลังทดลอง แนวทาง spec-as-source ที่จับคู่สเปกกับโค้ดแบบ 1:1
- ทั้งสามเครื่องมือ ต้องใช้กระบวนการที่หนักเกินไปสำหรับการแก้บั๊กเล็ก ๆ, การรีวิวไฟล์ Markdown ก็ยุ่งยากกว่าการรีวิวโค้ด และแม้จะมี context window ขนาดใหญ่ AI ก็ยังมีข้อจำกัดในการทำตามคำสั่งทั้งหมดได้อย่างถูกต้อง
- SDD ชวนให้นึกถึง กรณีล้มเหลวของ Model-Driven Development (MDD) ในอดีต และอาจเสี่ยงจะมีข้อเสียของทั้งสองฝั่งพร้อมกัน คือทั้งความไม่กำหนดแน่นอนและความไม่ยืดหยุ่น จึงยังต้องพิสูจน์ประโยชน์ในโครงการจริง
นิยามของ Spec-Driven Development (SDD)
- SDD คือแนวทางแบบ “documentation first” ที่เขียนสเปกก่อนโค้ด โดยให้สเปกเป็น single source of truth สำหรับทั้งนักพัฒนาและ AI
- GitHub ให้นิยามว่า “การบำรุงรักษาซอฟต์แวร์หมายถึง การวิวัฒน์ของสเปก โดยภาษากลางของการพัฒนาจะขยับไปอยู่ในระดับที่สูงขึ้น และโค้ดกลายเป็นสิ่งที่เข้าถึงในขั้นตอนสุดท้าย”
- Tessl อธิบายว่าเป็น “แนวทางการพัฒนาที่ สเปกกลายเป็นผลลัพธ์หลัก แทนที่จะเป็นโค้ด โดยสเปกใช้อธิบายเจตนาในภาษาที่มีโครงสร้างและทดสอบได้ และเอเจนต์จะสร้างโค้ดให้สอดคล้องกับสิ่งนั้น”
- SDD แบ่งเป็น 3 ระดับของการใช้งาน
- Spec-first: เขียนสเปกที่มีโครงสร้างให้ดีก่อน แล้วนำไปใช้ในเวิร์กโฟลว์การพัฒนาที่มี AI ช่วย
- Spec-anchored: หลังงานเสร็จแล้วยังคงเก็บรักษาสเปกไว้ เพื่อใช้ต่อในการพัฒนาและบำรุงรักษาฟีเจอร์นั้น
- Spec-as-source: สเปกยังคงเป็นไฟล์ต้นทางหลักแม้เวลาผ่านไป โดยนักพัฒนาแก้เฉพาะสเปกและไม่แตะโค้ดโดยตรง
- ทุกแนวทางของ SDD ล้วนเป็น spec-first แต่ไม่ใช่ทุกแนวทางจะมุ่งไปที่ spec-anchored หรือ spec-as-source และหลายกรณีก็ยัง ไม่ชัดเจนเรื่องกลยุทธ์การดูแลสเปกในระยะยาว หรือเปิดกว้างไว้ทั้งหมด
สเปก (spec) คืออะไร
- สเปกคือ ผลลัพธ์ที่มีโครงสร้าง เขียนด้วยภาษาธรรมชาติ และมุ่งอธิบายพฤติกรรม เพื่อแสดงความสามารถของซอฟต์แวร์และทำหน้าที่เป็นแนวทางให้ AI coding agent
- คำนิยามที่สอดคล้องกันที่สุดคือการเทียบสเปกกับ เอกสารความต้องการผลิตภัณฑ์ (PRD)
- สเปกควรถูกแยกออกจาก เอกสาร context ทั่วไป สำหรับ codebase
- context ทั่วไปประกอบด้วยไฟล์กฎ คำอธิบายระดับสูงของผลิตภัณฑ์และ codebase ซึ่งบางเครื่องมือเรียกว่า memory bank
- ไฟล์ใน memory bank เกี่ยวข้องกับทุก AI coding session ของ codebase แต่สเปกเกี่ยวข้องเฉพาะกับงานที่สร้างหรือแก้ไขฟีเจอร์หนึ่ง ๆ เท่านั้น
- แต่ละรูปแบบของ SDD จะกำหนดแนวทางเฉพาะของตัวเองเกี่ยวกับโครงสร้าง ระดับรายละเอียด และการจัดระเบียบสเปกภายในโครงการ
ความยากในการประเมินเครื่องมือ SDD
- การประเมินเครื่องมือและแนวทาง SDD ให้ ใกล้เคียงการใช้งานจริง ใช้เวลามาก
- ต้องลองกับปัญหาหลายขนาด ทั้งในโครงการ greenfield และ brownfield รวมถึงต้องใช้เวลารีวิวและแก้ไขผลลัพธ์ระหว่างทางอย่างจริงจัง ไม่ใช่แค่ดูผิวเผิน
- ในบล็อกโพสต์ของ GitHub เกี่ยวกับ spec-kit มีการย้ำว่า “สิ่งสำคัญคือบทบาทของคุณไม่ใช่แค่ชี้ทิศทาง แต่คือ การตรวจสอบยืนยัน และต้องสะท้อนกลับกับปรับปรุงในทุกขั้นตอน”
- จากสามเครื่องมือ มีสองตัวที่ดูเหมือนจะ ต้องใช้แรงมากกว่าในการนำเข้าสู่ codebase เดิม ทำให้ยิ่งประเมินประโยชน์กับ brownfield codebase ได้ยากขึ้น
- ก่อนจะได้ฟังรายงานจากผู้ที่ใช้กับ codebase จริงต่อเนื่องมาระยะหนึ่ง ก็ยังมี คำถามค้างคาอีกมากเกี่ยวกับการทำงานจริง
Kiro
- Kiro เป็นเครื่องมือที่ เรียบง่ายและเบาที่สุด ในสามตัว และส่วนใหญ่จัดอยู่ในแนวทาง spec-first
- พบเพียงตัวอย่างที่ใช้กับงานหรือ user story และยังไม่เห็นการกล่าวถึงวิธีใช้เอกสาร requirements แบบ spec-anchored ข้ามหลายงานเมื่อเวลาผ่านไป
- เวิร์กโฟลว์: requirements → design → tasks
- แต่ละขั้นของเวิร์กโฟลว์แสดงเป็นเอกสาร Markdown หนึ่งไฟล์ และ Kiro จะพาผู้ใช้ผ่านทั้ง 3 ขั้นภายในดิสทริบิวชันที่อิงกับ VS Code
-
องค์ประกอบหลักของ Kiro
- Requirements
- จัดโครงสร้างเป็น รายการ requirements โดยแต่ละข้อแทน “user story” (รูปแบบ As a...)
- acceptance criteria ใช้รูปแบบ GIVEN... WHEN... THEN...
- Design
- มีส่วนอย่างเช่นแผนภาพสถาปัตยกรรมคอมโพเนนต์, data flow, data model, การจัดการข้อผิดพลาด, กลยุทธ์การทดสอบ, แนวทางการ implement, กลยุทธ์ migration
- ยังไม่แน่ชัดว่าใช้โครงสร้างตายตัวหรือเปลี่ยนไปตามงาน
- Tasks
- เป็นรายการงานที่ติดตามด้วยหมายเลข requirement
- มี องค์ประกอบ UI เพิ่มเติม ที่ช่วยให้รันงานทีละข้อและรีวิวการเปลี่ยนแปลงของแต่ละงานได้
-
Memory bank ของ Kiro
- Kiro เรียกแนวคิด memory bank ว่า “steering”
- เนื้อหายืดหยุ่นได้ และดูเหมือนว่าเวิร์กโฟลว์ไม่ได้ขึ้นอยู่กับการมีอยู่ของไฟล์เฉพาะเจาะจง
- โครงสร้างพื้นฐาน ที่ Kiro สร้างเมื่อถูกขอให้สร้างเอกสาร steering คือ product.md, structure.md, tech.md
Spec-kit
- spec-kit คือ เวอร์ชัน SDD ของ GitHub โดยแจกจ่ายเป็น CLI ที่สามารถสร้างการตั้งค่า workspace สำหรับ coding assistant ทั่วไปหลายแบบได้
- หลังตั้งค่าโครงสร้างแล้ว จะโต้ตอบกับ spec-kit ผ่าน slash command ของ coding assistant
- เนื่องจากผลลัพธ์ทั้งหมดถูกวางไว้ใน workspace โดยตรง จึงเป็นเครื่องมือที่ ปรับแต่งได้มากที่สุด ในบรรดาสามตัวที่กล่าวถึง
-
เวิร์กโฟลว์ของ Spec-kit
- เวิร์กโฟลว์: Constitution → 𝄆 Specify → Plan → Tasks 𝄇
- แนวคิด memory bank ของ spec-kit เป็น เงื่อนไขตั้งต้นของแนวทาง spec-driven
- เรียกสิ่งนี้ว่า constitution และประกอบด้วยหลักการระดับสูงแบบ “ไม่เปลี่ยนแปลง” ที่ต้องใช้กับทุกการเปลี่ยนแปลงเสมอ
- เป็นไฟล์กฎที่ทรงพลังมากและถูกใช้หนักในเวิร์กโฟลว์
-
วิธีทำงานของ Spec-kit
- ในแต่ละขั้นของเวิร์กโฟลว์ (specify, plan, tasks) จะ ใช้ bash script และ template เพื่อสร้างชุดไฟล์และพรอมป์ต์ขึ้นมาเป็นอินสแตนซ์
- เวิร์กโฟลว์ใช้ checklist ภายในไฟล์จำนวนมากเพื่อติดตามความชัดเจนที่ต้องถามผู้ใช้, การละเมิด constitution, งานวิจัย ฯลฯ
- ทำหน้าที่คล้าย “definition of done” ของแต่ละขั้น (แต่เพราะ AI เป็นผู้ตีความ จึงไม่ได้รับประกัน 100%)
- หนึ่งสเปก ประกอบด้วยหลายไฟล์
- เช่น data-model, plan, tasks, spec, research, api, component รวม 8 ไฟล์
-
แนวทางของ Spec-kit
- ในตอนแรก GitHub ดูเหมือนจะ มุ่งไปที่แนวทาง spec-anchored
- “เรากำลังคิดใหม่ว่าสเปกไม่ใช่เอกสารคงที่ แต่เป็นผลลัพธ์ที่มีชีวิตและใช้งานได้จริงซึ่งวิวัฒน์ไปพร้อมโครงการ และสเปกจะกลายเป็นแหล่งความจริงร่วมกัน”
- แต่เนื่องจาก spec-kit สร้าง branch สำหรับทุกสเปกที่ถูกสร้าง จึงอาจตีความได้ว่ามองสเปกว่าเป็นผลลัพธ์ที่มีชีวิตอยู่เพียง ตลอดอายุของ change request มากกว่าตลอดอายุของฟีเจอร์
- ในการพูดคุยของชุมชนก็มีการกล่าวถึงความสับสนนี้ และดูเหมือนว่า spec-kit ยังเป็นเพียง spec-first และไม่ได้เป็น spec-anchored เมื่อเวลาผ่านไป
Tessl Framework
- Tessl Framework ยังอยู่ใน private beta และแจกจ่ายเป็น CLI ที่สามารถสร้างโครงสร้าง workspace และ config สำหรับ coding assistant หลายแบบได้เช่นเดียวกับ spec-kit
- คำสั่ง CLI ยัง ทำงานเป็น MCP server ได้ด้วย
-
จุดเด่นของ Tessl
- เป็นเครื่องมือเดียวในสามตัวที่ ระบุชัดว่ามุ่งสู่แนวทาง spec-anchored และกำลังสำรวจ SDD ระดับ spec-as-source ด้วย
- สเปกของ Tessl สามารถเป็นผลลัพธ์หลักที่นำมาดูแลและแก้ไขต่อได้ โดยโค้ดจะมีคอมเมนต์
// GENERATED FROM SPEC - DO NOT EDIT อยู่ด้านบน
- ปัจจุบันเป็นการ จับคู่แบบ 1:1 ระหว่างสเปกและไฟล์โค้ด กล่าวคือหนึ่งสเปกจะถูกแปลงเป็นหนึ่งไฟล์ใน codebase
- แต่ยังอยู่ในช่วงเบต้าและกำลังทดลองหลายเวอร์ชัน จึงอาจพัฒนาไปถึงระดับที่หนึ่งสเปกจับคู่กับคอมโพเนนต์โค้ดที่มีหลายไฟล์ได้
-
โครงสร้างสเปกของ Tessl
- แท็กอย่าง
@generate หรือ @test ใช้เพื่อ สั่ง Tessl ว่าควรสร้างอะไร
- ส่วน API แสดงแนวคิดของการนิยามอินเทอร์เฟซขั้นต่ำที่เปิดเผยต่อส่วนอื่นของ codebase ไว้ในสเปก เพื่อให้แน่ใจว่าส่วนสำคัญของคอมโพเนนต์ที่สร้างขึ้น ยังอยู่ภายใต้การควบคุมเต็มที่ของผู้ดูแล
- เมื่อรัน
tessl build ก็จะสร้างไฟล์โค้ด JavaScript ตามนั้น
-
ระดับนามธรรมของ Tessl
- ถ้าวางสเปกสำหรับ spec-as-source ไว้ที่ ระดับนามธรรมค่อนข้างต่ำต่อหนึ่งไฟล์โค้ด ก็จะลดจำนวนขั้นตอนและการตีความที่ LLM ต้องทำ และลดโอกาสเกิดข้อผิดพลาดตามไปด้วย
- ถึงแม้อยู่ที่ระดับนามธรรมต่ำเช่นนี้ ก็ยังพบ ความไม่กำหนดแน่นอนเมื่อสร้างโค้ดจากสเปกเดียวกันหลายครั้ง
- กระบวนการเขียนสเปกซ้ำ ๆ และทำให้มันเฉพาะเจาะจงขึ้นเรื่อย ๆ เพื่อเพิ่มความสามารถในการสร้างโค้ดซ้ำได้แบบเดิม ทำให้นึกถึง กับดักและความท้าทายของการเขียนสเปกที่ไม่กำกวมและครบถ้วน
ข้อสังเกตและคำถาม
-
เวิร์กโฟลว์เดียวครอบคลุมทุกขนาดงานได้หรือไม่?
- Kiro และ spec-kit ต่างก็มี เวิร์กโฟลว์แบบตายตัวของตัวเอง แต่ทั้งคู่มีแนวโน้มว่าจะไม่เหมาะกับปัญหาการเขียนโค้ดส่วนใหญ่ในโลกจริง
- ยังไม่ชัดเจนว่าให้ ความหลากหลายเพียงพอตามขนาดของปัญหา หรือไม่
- ตอนลองใช้ Kiro แก้บั๊กเล็ก ๆ เวิร์กโฟลว์ให้ความรู้สึกเหมือน ใช้ค้อนทุบเปลือกวอลนัต
- เอกสาร requirements แปลงบั๊กเล็ก ๆ ให้กลายเป็น “user story” 4 ข้อ พร้อม acceptance criteria รวม 16 ข้อ
- ตอนใช้ spec-kit ก็ยัง ไม่แน่ใจว่าปัญหาขนาดไหนถึงควรใช้
- เมื่อลองทำฟีเจอร์ที่ในอดีตทีมอาจประเมินเป็น story ขนาด 3-5 points จำนวนขั้นตอนและปริมาณไฟล์ Markdown ที่ spec-kit สร้างขึ้นก็ดูมากเกินไปเมื่อเทียบกับขนาดงาน
- ในเวลาเดียวกันนั้น หากใช้การเขียนโค้ดแบบมี AI ช่วยในแบบ “ทั่วไป” ก็น่าจะทำฟีเจอร์เสร็จได้ และรู้สึกควบคุมได้มากกว่าด้วย
- เครื่องมือ SDD ที่มีประสิทธิภาพควร ยืดหยุ่นพอให้มีเวิร์กโฟลว์หลักหลายแบบ สำหรับการเปลี่ยนแปลงที่หลากหลายทั้งขนาดและประเภท
-
รีวิวย์ Markdown แทนรีวิวโค้ดงั้นหรือ?
- spec-kit สร้าง ไฟล์ Markdown จำนวนมากให้ต้องรีวิว
- หลายไฟล์ซ้ำกันเองและซ้ำกับโค้ดที่มีอยู่แล้ว
- บางไฟล์มีโค้ดรวมอยู่ด้วย ทำให้โดยรวมยืดยาวมากและน่าเบื่อในการรีวิว
- ใน Kiro จะได้เพียง 3 ไฟล์ และ mental model ของ “requirements > design > tasks” เข้าใจง่ายกว่าโดยสัญชาตญาณ จึงง่ายขึ้นเล็กน้อย
- แต่ Kiro ก็ยังยืดยาวเกินไปเมื่อเทียบกับบั๊กเล็ก ๆ ที่ขอให้แก้
- พูดตรง ๆ คือรู้สึกว่า รีวิวโค้ดน่าจะดีกว่ารีวิวไฟล์ Markdown ทั้งหมดนี้
- เครื่องมือ SDD ที่ดีจำเป็นต้องมอบ ประสบการณ์การรีวิวสเปกที่ดีมากจริง ๆ
-
ความรู้สึกว่าควบคุมได้ที่ไม่จริงหรือเปล่า?
- แม้จะมีไฟล์ เทมเพลต พรอมป์ต์ เวิร์กโฟลว์ และ checklist ทั้งหมดนี้ ก็ยังเห็นอยู่บ่อยครั้งว่า ท้ายที่สุดเอเจนต์ก็ไม่ได้ทำตามทุกคำสั่ง
- context window ที่ใหญ่ขึ้นมักถูกพูดถึงว่าเป็นปัจจัยที่ทำให้ SDD เกิดขึ้นได้ แต่การที่ window ใหญ่ขึ้นไม่ได้แปลว่า AI จะเข้าใจทุกอย่างในนั้นได้ถูกต้อง
- ตัวอย่าง
- spec-kit มีขั้นตอน research ระหว่างการวางแผนและได้ศึกษาจากโค้ดเดิมไปมาก แต่สุดท้ายเอเจนต์กลับไม่สนใจว่าสิ่งนั้นคือคำอธิบายของคลาสเดิม และตีความเป็นสเปกใหม่ ก่อนจะสร้างทุกอย่างซ้ำจนเกิดความซ้ำซ้อน
- ไม่ใช่แค่กรณีที่เพิกเฉยต่อคำสั่งเท่านั้น แต่ยังพบเอเจนต์ที่พยายามทำตามคำสั่งมากเกินไป เช่น ทำเกินเลย เพื่อให้สอดคล้องกับข้อหนึ่งใน constitution
- จากประสบการณ์ที่ผ่านมา วิธีที่ดีที่สุดในการควบคุมสิ่งที่เราสร้างคือ ขั้นตอนเล็ก ๆ และทำซ้ำได้ จึงค่อนข้างสงสัยมากว่าการออกแบบสเปกล่วงหน้าจำนวนมากจะเป็นความคิดที่ดีหรือไม่
- เครื่องมือ SDD ที่มีประสิทธิภาพควร รองรับแนวทางแบบ iterative แต่แพ็กเกจงานเล็ก ๆ ก็ดูเกือบจะขัดกับแนวคิดของ SDD เอง
-
จะแยก functional spec กับ technical spec อย่างมีประสิทธิภาพได้อย่างไร?
- ใน SDD การ แยกระหว่างสเปกเชิงหน้าที่กับการ implement ทางเทคนิค เป็นแนวคิดที่ตั้งใจทำอย่างชัดเจน
- ความใฝ่ฝันเชิงลึกคือท้ายที่สุด AI จะเติมคำตอบและรายละเอียดทุกอย่างให้ได้ และสามารถสลับไปใช้ tech stack อื่นโดยอิงจากสเปกเดียวกัน
- แต่ในทางปฏิบัติ เมื่อลองใช้ spec-kit ก็มักสับสนว่า เมื่อไรควรอยู่ในระดับฟังก์ชันล้วน ๆ และเมื่อไรควรใส่รายละเอียดทางเทคนิค
- ทั้ง tutorial และเอกสารก็ไม่ค่อยสอดคล้องกันในเรื่องนี้ และมีการตีความต่างกันว่า “functional ล้วน ๆ” หมายถึงอะไร
- เมื่อนึกถึง user story จำนวนมาก ที่แยก requirements ออกจาก implementation ได้ไม่ดี ก็ต้องยอมรับว่าวิชาชีพของเราเองก็ไม่ได้มีประวัติที่ดีนักในเรื่องนี้
-
แล้วผู้ใช้เป้าหมายคือใคร?
- เดโมและ tutorial ของเครื่องมือพัฒนาแบบ spec-driven หลายตัวมีสิ่งอย่างการกำหนดเป้าหมายผลิตภัณฑ์และฟีเจอร์ รวมถึง ใช้คำอย่าง “user story”
- แนวคิดตรงนี้อาจเป็นการใช้ AI เป็นตัวช่วยเรื่อง cross-skilling เพื่อให้นักพัฒนา มีส่วนร่วมกับการวิเคราะห์ requirements มากขึ้น
- หรือให้นักพัฒนาจับคู่ทำงานกับ product owner ภายใต้เวิร์กโฟลว์นี้?
- แต่ไม่มีข้อไหนถูกอธิบายไว้อย่างชัดเจน และทุกอย่างถูกนำเสนอราวกับว่านักพัฒนาควรเป็นผู้ทำการวิเคราะห์เหล่านี้เองตามธรรมชาติ
- ถ้าอย่างนั้น SDD เหมาะกับปัญหาขนาดและประเภทไหนกันแน่?
- มันอาจไม่เหมาะกับฟีเจอร์ขนาดใหญ่ที่ยังคลุมเครือมาก เพราะในกรณีนั้นยังต้องการทักษะด้าน product และ requirements ที่เชี่ยวชาญกว่า รวมถึงขั้นตอนอื่นอีกมาก เช่น research และการมีส่วนร่วมของผู้มีส่วนได้ส่วนเสีย
-
Spec-anchored และ spec-as-source: เรากำลังเรียนรู้จากอดีตหรือไม่?
- หลายคนเปรียบเทียบ SDD กับ TDD หรือ BDD แต่โดยเฉพาะในกรณี spec-as-source ยังมีอีกหนึ่งความคล้ายคลึงสำคัญที่ควรพิจารณา นั่นคือ MDD (Model-Driven Development)
- ผู้เขียนเคยทำงานกับโครงการที่ใช้ MDD อย่างมากในช่วงต้นอาชีพ และเมื่อลอง Tessl Framework ก็ทำให้นึกถึงเรื่องนี้ตลอด
- โมเดลใน MDD โดยพื้นฐานแล้วก็คือสเปก แต่ถูกแสดงออกใน UML แบบปรับแต่งเองหรือ DSL เชิงข้อความ ไม่ใช่ภาษาธรรมชาติ
- และมีการสร้างตัวสร้างโค้ดแบบเฉพาะขึ้นมาเพื่อแปลงสเปกเหล่านั้นให้เป็นโค้ด
-
เปรียบเทียบ MDD กับ SDD
- ท้ายที่สุดแล้ว MDD ไม่ประสบความสำเร็จในงาน business application เพราะอยู่บนระดับนามธรรมที่ดูขัด ๆ และสร้างทั้ง overhead และข้อจำกัดมากเกินไป
- แต่ LLM ช่วยลด overhead และข้อจำกัดบางส่วนของ MDD ลง จึงทำให้เกิดความหวังใหม่ว่าเราจะ โฟกัสที่การเขียนสเปกและให้มันสร้างโค้ด ได้
- เมื่อใช้ LLM ก็ไม่ต้องถูกจำกัดอยู่กับภาษาสเปกที่ต้องนิยามและ parse ได้ล่วงหน้า และไม่จำเป็นต้องสร้าง code generator ที่ซับซ้อน
- แน่นอนว่าราคาที่ต้องจ่ายคือ ความไม่กำหนดแน่นอนของ LLM
- โครงสร้างที่ parse ได้ก็มีข้อดีตรงที่สามารถให้การสนับสนุนเครื่องมือจำนวนมากแก่ผู้เขียนสเปกในการเขียนสเปกที่ถูกต้อง สมบูรณ์ และสอดคล้องกัน ซึ่งตอนนี้เรากำลังสูญเสียจุดนั้นไป
- spec-as-source หรือแม้แต่ spec-anchoring ก็อาจลงเอยด้วยการมี ข้อเสียของทั้ง MDD และ LLM พร้อมกัน: ความไม่ยืดหยุ่น และ ความไม่กำหนดแน่นอน
- เราควรย้อนดูความพยายามแบบ spec-from-code ในอดีต และเรียนรู้จากสิ่งนั้นเมื่อสำรวจแนวทาง spec-driven ในปัจจุบัน
บทสรุป
- โดยส่วนตัวแล้ว เมื่อใช้การเขียนโค้ดที่มี AI ช่วย ผู้เขียนมักใช้เวลาไปกับการ เขียนสเปกในรูปแบบที่จะป้อนให้ coding agent อย่างรอบคอบ
- ดังนั้นหลักการกว้าง ๆ ของ spec-first จึงมีคุณค่าแน่นอนในหลายสถานการณ์
- ยังมีความต้องการอย่างมากต่อแนวทางต่าง ๆ ในการจัดโครงสร้างสเปก และนี่เป็นหนึ่งในคำถามที่พบบ่อยที่สุดจากผู้ปฏิบัติงานในตอนนี้
- “จะจัดโครงสร้าง memory bank อย่างไร?”, “จะเขียนสเปกและเอกสาร design ที่ดีสำหรับ AI ได้อย่างไร?”
- อย่างไรก็ตาม คำว่า “spec-driven development” ยังไม่ได้ถูกนิยามอย่างชัดเจน และความหมายก็เริ่มกระจายออกไปแล้ว
- ช่วงหลังยังได้ยินคนใช้คำว่า “spec” เป็นเสมือน คำพ้องของ “พรอมป์ต์แบบละเอียด” ด้วยซ้ำ
-
การประเมินเครื่องมือ
- บางเครื่องมือพยายามถ่ายทอดเวิร์กโฟลว์เดิมให้ AI agent แบบตรงตัวเกินไป ซึ่งท้ายที่สุดอาจ ขยายปัญหาเดิมอย่าง review overload และ hallucination ให้หนักขึ้น
- โดยเฉพาะแนวทางที่ซับซ้อนและสร้างไฟล์จำนวนมาก ทำให้นึกถึงคำประสมภาษาเยอรมัน “Verschlimmbesserung” (พยายามปรับปรุงแต่กลับทำให้แย่ลง)
- จนอดสงสัยไม่ได้ว่าในการพยายามทำให้ดีขึ้นนั้น เรากำลังทำให้บางอย่างแย่ลงหรือไม่
3 ความคิดเห็น
ก่อนหน้านี้ก็ดูคล้ายกับแนวคิดอย่าง document driven develop หรือ readme driven develop นะครับ
https://th.news.hada.io/topic?id=15502
ความคิดเห็นจาก Hacker News
ช่วงนี้กำลังติดตามเทรนด์ SDD (การพัฒนาที่ขับเคลื่อนด้วยสเปก) อยู่ รู้สึกว่าแนวทางนี้มีเหตุผล แต่ก็ให้ความรู้สึกราวกับกำลังย้อนกลับไปสู่ยุคก่อน Agile ที่เต็มไปด้วยเอกสารสเปกฟีเจอร์และเอกสารออกแบบ ไม่ถึงกับเป็น Big Design Up Front แบบเต็มตัว แต่ดูเหมือนกำลังค่อย ๆ ไปสู่ภาพที่ว่า “ซอฟต์แวร์ที่ทำงานได้ == เอกสารที่สมบูรณ์แบบ”
ดู Big Design Up Front, ดู Agile Manifesto
เอกสารสเปกฟังก์ชันและเอกสารออกแบบสุดท้ายแล้วก็ไม่ต่างจากการเขียนโค้ดด้วยภาษาธรรมชาติ แต่ก่อนมนุษย์ต้องนำสิ่งนี้ไปเขียนใหม่เป็นภาษาโปรแกรม ตอนนี้คอมไพเลอร์อัตโนมัติอย่าง LLM (large language model) เริ่มเข้ามาทำแทนให้แล้ว จึงเหมือนข้ามขั้นตอนไปได้หนึ่งขั้น (แม้อัตราความสำเร็จจะต่างกันไป)<br /> ในทางกลับกัน Agile ไม่ได้สนใจว่าซอฟต์แวร์จะถูกสร้างด้วยภาษาอะไร แก่นสำคัญคือ “ตัดผู้จัดการออก” แล้วให้นักพัฒนารับหน้าที่ด้านการจัดการเองด้วย โดย 12 หลักการ อธิบายรายละเอียดมากขึ้นว่าหากไม่มีผู้จัดการ นักพัฒนาควรทำอะไรบ้าง
Behaviour Driven Design สามารถสร้าง “สเปกที่มีชีวิต” ได้คล้ายกับ Test Driven Design โดยทิ้งร่องรอยไว้เป็นเอกสารที่มนุษย์อ่านได้ ตั้งแต่เกณฑ์ในการสำรวจโดเมนไปจนถึงการผ่านการทดสอบ และยังเชื่อมตรงกับ test harness เพื่อตรวจสอบฟังก์ชันปัจจุบันได้ แบบนี้จะได้เอกสารสเปกที่ตรวจสอบได้ชัดเจน ทำงานร่วมกันและทำซ้ำได้ผ่านรายงาน BDD พร้อมทั้งยังรักษาแนวคิดแบบ Agile, JIT และ YAGNI ได้โดยไม่ต้องทำงานเกินความจำเป็นตั้งแต่ต้น จึงไม่จำเป็นต้องย้อนกลับไปใช้ waterfall
เราอาจเริ่มจากการออกแบบขนาดเล็กได้ เช่น เขียนสเปกแค่หนึ่งถึงสองหน้า แล้วใช้ LLM สร้างโค้ดกับเทสต์ จากนั้นค่อยปรับปรุงแบบวนซ้ำ หากเชื่อใจการเขียนโค้ดด้วย LLM 100% การจัดการอินพุตสเปก (English prompt) อย่างเป็นระบบก็ถือว่าสมเหตุสมผล การไม่ทิ้งพรอมป์ต์และจัดระเบียบไว้ จะช่วยให้เอาไปใช้ซ้ำในอนาคตหรือเพิ่มข้อจำกัดใหม่ ๆ ได้ชัดเจนขึ้น<br /> แต่ส่วนตัวยังไม่มั่นใจที่จะใช้ LLM กับโค้ด production สำคัญ ๆ เลยยังทดลองแค่ใน sandbox/test/demo เท่านั้น ใช้เฉพาะงานที่คุณภาพโค้ดไม่สำคัญมาก
Spec driven development เองเป็นไอเดียที่ดี แต่ implementation ตอนนี้มักแค่ส่งไฟล์ markdown ที่ไม่มีโครงสร้างให้ agent แล้วผลลัพธ์ก็ออกมาไม่แน่นอนจนแทบไม่มี reproducibility จริง ๆ ถ้า agent จะเขียนสเปกด้วย สเปกก็ควรมีโครงสร้างที่สามารถแปลงเป็น stub code และ test code ได้อัตโนมัติ แทนที่จะทิ้ง markdown ไปเฉย ๆ ถ้านำไปเชื่อมกับ code generator ได้ reproducibility จะดีขึ้นมาก และหากทำแบบนั้นจริง เวลาที่เสียไปกับการสร้างโค้ดซ้ำ ๆ ก็น่าจะลดลงอย่างมาก
ลองใช้ SpecKit แล้วรู้สึกว่าน่าสนใจและน่าพอใจมาก แต่ก็ลำบากในการหาตัวอย่างหรือวิธีใช้งานที่ซับซ้อนในโลกจริง บทสอนส่วนใหญ่หยุดอยู่แค่ระดับติดตั้งแล้ว “ทำแอป todo list” อยากให้มีการแชร์กรณีจริงของการค่อย ๆ ปรับปรุงหรือรีแฟกเตอร์ legacy codebase เดิม หรือวิธีเข้าหาโปรเจกต์ขนาดใหญ่ที่ไม่ได้เริ่มต้นด้วย spec driven development ตั้งแต่แรก
ตัวผมเองก็รู้สึกชัดเช่นกันว่าการจดบันทึกฟีเจอร์ด้วยโค้ดจริงโดยตรง ขณะใช้แนวทาง BDD ควบคู่กับการเขียนโค้ดผ่าน CLI มีประสิทธิภาพกว่าการเขียน markdown ยาว ๆ มาก ถ้าต้องมี checklist ใหญ่ให้ AI ทำตาม แค่มีไฟล์อย่าง agents.md ก็เพียงพอแล้ว ขอแค่บันทึก coding pattern และ non-functional requirements (NFR) ไว้สักครั้ง agent ก็จะทำตามได้ชัดเจน
ถ้าจะใช้งานจริง ก็ต้องอ่าน template หรือ source code ด้วยตัวเองถึงจะเข้าใจว่าจริง ๆ แล้วมีอะไรทำงานอยู่บ้าง ซึ่งสำหรับโปรเจกต์แบบนี้ ผมมองว่าเป็นกระบวนการที่แทบเลี่ยงไม่ได้อยู่แล้ว
พอเห็นมีการแนะนำผู้เชี่ยวชาญด้านการส่งมอบงานด้วย AI จาก Thoughtworks แล้วต่อด้วยเรื่อง memory banks ก็รู้สึกอินทันที เพราะทีมเราก็ทำงานในสนามจริงที่ “AI คือกระแสหลัก” และพบว่าเมื่อ memory bank โตขึ้น AI กลับยิ่งหลงทิศ และผลลัพธ์ก็ยิ่งปะปนมั่วไปหมด สุดท้ายคนที่เข้าใจผลิตภัณฑ์อย่างแท้จริงต้องเป็นคนคอยนำ AI เอง ถึงจะนำไปใช้หน้างานได้จริง ผมเจอมาแล้วนับไม่ถ้วนว่า AI หลอกผมแค่ไหน และพอมนุษย์เข้าไปชี้ทางใหม่ มันก็จะขอโทษและบอกว่าเข้าใจ แต่สุดท้ายก็ไม่ได้เปลี่ยนอะไร การให้ AI ไปอ่านและตรวจเอกสารจำนวนมหาศาลที่พวกมันสร้างกันเองก็ไม่สมจริง ในแง่นี้เอาเวลาไปลงมือทำเองยังดีกว่าเยอะ
สงสัยว่า memory bank หมายถึงอะไรกันแน่ และอยากสื่อถึงบทบาทของมันอย่างเป็นรูปธรรมแบบไหน
ณ ตอนนี้ความสามารถด้าน memory กลับส่งผลเสียมากกว่า หากใช้โดยไม่มี strategy ด้าน search/retrieval ที่ดี มันจะยิ่งขยายผลลัพธ์แย่ ๆ ออกมาเท่านั้น ระบบ memory ส่วนใหญ่ออกแบบมาสำหรับพาราไดม์แบบแชตเดียว จึงก่อปัญหาเมื่อใช้ในสภาพแวดล้อมที่หลากหลาย “ความทรงจำ” ที่แท้จริงควรเป็นรูปแบบ ‘meta-cognition/default mode network’ ที่แยกจาก main agent ใช้ออกแบบโครงสร้างงานและบริบทแบบ asynchronous แล้วให้ meta model นี้คอยกำหนดทิศทางให้แต่ละพรอมป์ต์ กล่าวคือควรเป็น ‘agent-based memory’
ผมก็ไม่เข้าใจเหมือนกันว่าการติดตำแหน่งว่า ‘thought leader’ ไว้บน LinkedIn อะไรทำนองนั้นหมายถึงอะไร ทุกวันนี้รู้สึกว่ายากจริง ๆ ที่จะเข้าใจว่าตำแหน่งแบบนั้นมีความหมายอะไร
อยากแชร์ประสบการณ์ช่วง 2 สัปดาห์ล่าสุดที่ทดลองใช้ SpecKit กับ Claude Code ในโปรเจกต์ใหม่สองตัว เนื่องจากทั้งสองโปรเจกต์ผมพัฒนาคนเดียวทั้งหมดเลยกล้าทดลองได้ โปรเจกต์แรกปล่อยให้ SpecKit จัดการตามแนวทางของมันตรง ๆ ใช้เวลา 10 วันกว่าจะทำทุก task เสร็จ แต่เทสต์ส่วนใหญ่ของผลลัพธ์กลับล้มเหลวและ build ก็พัง ต้องใช้เวลาอีกพอ ๆ กันเพื่อแก้ปัญหา และสุดท้าย Claude ก็แก้ตรงหนึ่งแล้วไปทำอีกส่วนพัง ทำให้ความน่าเชื่อถือลดลงอย่างมาก<br /> โปรเจกต์ที่สอง ผมอยากทำแบบวนซ้ำเป็นหน่วยเล็ก ๆ จึงเพิ่ม slash command หลังจบขั้น planning ของ SpecKit เพื่อสร้าง backlog.md จากนั้นใช้ plan-sprint สร้างไฟล์ที่มีทั้งเป้าหมายสปรินต์และรายละเอียดงาน แล้วใช้ implement-sprint ประมวลผลทีเดียว แต่กระบวนการนี้ก็ยังไม่ทำตามคำสั่ง implementation อยู่ดี ต่อให้แก้กระบวนการหลายครั้งก็ยังไม่สร้างเทสต์ หรือทำ task ตกหล่นอยู่เรื่อย ๆ<br /> เลยปรับเซ็ตอัปอีกครั้ง โดยแยก subagent สำหรับ task เฉพาะ และใช้ implement-sprint เป็นแค่ orchestrator แบบนี้จะตรวจสถานะโค้ดได้ในแต่ละสปรินต์ จึงเชื่อถือได้มากขึ้น แม้ตอนนี้มันยังมีบ้างที่บอกว่าเสร็จแล้วทั้งที่เทสต์ยังไม่ผ่าน แต่ก็ยังง่ายกว่าการต้องมานั่งไล่ดูทั้งหมดเอง<br /> สมมติฐานตอนนี้คือ Claude อ่อนด้าน TDD เพราะมักมีปัญหาเสมอในขั้นที่ต้องเขียนเทสต์ก่อน ครั้งหน้าจึงตั้งใจจะลองเปลี่ยนไปเขียนเทสต์หลัง implementation เสร็จ แม้จะไม่ใช่วิธีที่อุดมคติ แต่ถ้าทำแบบนี้แล้ว productivity สูงกว่าการเขียนเองก็น่าจะคุ้ม
น่าสนใจที่หลายเครื่องมือส่วนใหญ่ออกแบบมาสำหรับกลยุทธ์ “สเปกก่อน” แต่กลับคลุมเครือเรื่องการดูแลรักษาสเปก ส่วนตัวตอนนี้กำลัง all-in กับแนวคิด ‘spec as source’ ร่วมกับ cofounder ผมคิดว่านี่คือ use case ที่น่าสนใจที่สุดของการทำให้สเปกเป็น source จริง ๆ และกำลังพยายามอยู่ แต่การทำให้มันปักหลักได้จริงนั้นยากมาก ถ้าใครมีประสบการณ์หรือฟีดแบ็กเกี่ยวกับเรื่องนี้ก็อยากฟังมาก<br /> ถ้าสนใจ specific.dev ซึ่งเป็นสนามทดลองของเรา ก็ลองใช้แล้วบอกความเห็นกันได้
ลองใช้ workflow แบบ spec-driven ของ Kiro แล้วพบว่ามันสร้างรายการงานออกมามหาศาล (แต่ละงานมีอย่างน้อย 4 subtask และรวมทั้งหมดเกิน 12 งาน) ตัว workflow เองถือว่าโอเค แต่ปัญหาคือมันลบโค้ดแบบคาดเดาไม่ได้และไม่ rollback ให้ด้วย ดูเหมือนเพราะทำเป็น IDE เลยเสียทรัพยากรไปกับการจัดการ UI edge case มากเกินไป เมื่อเทียบกันแล้ว การทำซ้ำแบบง่าย ๆ แล้วค่อยปรับปรุงกลับง่ายกว่า เป็นลักษณะ “ใช้ค้อนใหญ่ทุบวอลนัต”
ชอบวิธีใช้ custom slash command เพื่อห่ออินพุตให้เป็นพรอมป์ต์ที่มีโครงสร้างดี และยังดีที่ดึงส่วนที่แยกไว้จาก agents.md หรือที่อื่นมาฉีดเป็น context ร่วมด้วย<br /> แต่ผมไม่ชอบส่วนที่พยายามแก้ทุกปมจนกลายเป็นเหมือนใช้ค้อนทุบวอลนัตอีกแบบหนึ่ง คือสร้างความซับซ้อนเพิ่มขึ้น อย่างไรก็ตาม ถ้าสามารถเพิ่ม slash command เฉพาะเวลาจำเป็น (/experiment, /stub เป็นต้น) เพื่อเลือกจัดการ context แบบเบา ๆ ได้ ก็น่าจะดีขึ้น และปิดท้ายด้วยคำสั่งสำหรับสรุปงานอย่าง "/wrap-up" เพื่อรวบทุกปมเข้าด้วยกันทีเดียว คล้ายกับหมอที่ตรวจภาพรวมทั้งครั้งหลังผ่าตัดเสร็จ
ตอนใช้ SpecKit ผมมีคำถามอยู่ตลอดว่า “เมื่อไหร่สเปกทั้งหมดพวกนี้จะถูกรวมเป็นมาตรฐานจริงหนึ่งเดียว (ground truth)?” น่าเสียดายที่ดูเหมือนจะไม่มีขั้นนั้นเลย ตอนนี้จึงเหลือโจทย์เรื่องการนิยาม/ออกแบบว่าสเปกสำหรับการสื่อสารระหว่างมนุษย์กับ agent ควรมีหน้าตาแบบไหน ผมกำลังคิดต่อว่าจะทำแนวคิด ‘spec anchored’ ที่ Birgitta พูดถึงให้เกิดขึ้นจริงได้อย่างไร
ไม่รู้เหมือนกันว่าทำไม SDD ถึงกลายเป็นกระแสขึ้นมาแบบกะทันหัน แต่สำหรับผม มันมีประโยชน์ในเชิงปฏิบัติก็แค่การสร้างไฟล์สเปกเพื่อ “เห็นผลลัพธ์สุดท้ายล่วงหน้า” และใช้ติดตามความคืบหน้าเวลาย่อยโปรเจกต์เป็นชิ้นเล็ก ๆ ผมไม่ได้ใช้เครื่องมือหรือเฟรมเวิร์กอะไรเป็นพิเศษ ใช้แค่ไฟล์ markdown เท่านั้น และก็ไม่ค่อยรู้สึกว่าระบบที่ซับซ้อนกว่านั้นมีคุณค่าเพิ่มอะไร
ตอนเริ่มใช้ spec-kit ครั้งแรก ผมคาดหวังไว้มาก แต่พอใช้จริงมันกลับสร้างงานที่เกินจำเป็นราวกับต้อง “สร้างหุ่นยนต์ในถ้ำ” ทั้งที่บางงานแค่ขันน็อตให้แน่นก็พอ มันขยายเรื่องง่ายให้ใหญ่เกินไป จนสุดท้ายผมเหนื่อยกับการไล่แก้แล้วก็เลิกใช้ไป รู้สึกว่า workflow ที่ซับซ้อนเกินเหตุแบบนี้ไม่คุ้มแม้แต่จะพยายามชดเชยมัน
จริง ๆ แล้วถ้าคิดว่าเป็นการนำสิ่งที่เรียนในวิศวกรรมซอฟต์แวร์มาทำในรูปแบบ Markdown ก็จะเข้าใจได้ง่ายและใช้งานได้จริง เพียงแค่เขียนเอกสารข้อกำหนดความต้องการให้ดีก็พอแล้ว