Ubiquitous Language: file context แรกที่ควรป้อนให้ coding agent — ก่อน spec
บทความนี้เป็น บทความต้นฉบับของแก่น (Kaen) ไม่ใช่บทแปล — เป็นการเรียบเรียงความคิดเห็นเชิงปฏิบัติเกี่ยวกับการทำงานกับ AI coding agent บนฐานของแนวคิด Domain-Driven Design ที่ไซต์นี้สอนไว้แล้ว
บทความต่อยอดจากเนื้อหาภายในไซต์โดยตรง ได้แก่
- บทเรียน Ubiquitous Language ในคอร์ส DDD
- หน้าอ้างอิง Ubiquitous Language และ Bounded Context ในสารานุกรม DevIQ
- บทความสองชิ้นก่อนหน้าเรื่อง Spec-Driven Development และ Structured-Prompt-Driven Development (SPDD)
ศัพท์เทคนิคคงไว้เป็นภาษาอังกฤษและอธิบายเป็นไทยเมื่อพบครั้งแรก
ก่อนจะบอก agent ว่า “ให้ทำอะไร” คุณต้องบอกก่อนว่า “เราเรียกสิ่งต่าง ๆ ว่าอะไร”
ลองนึกภาพนี้ คุณเปิด coding agent ขึ้นมา เขียน spec ที่ละเอียดพอสมควรว่าอยากได้ระบบคิดค่าบริการแบบไหน แล้วปล่อยให้มันลงมือ ห้านาทีต่อมา code ก็ออกมา ใช้งานได้ ผ่าน test ที่มันเขียนเอง
แต่พอเปิด file ดูจริง ๆ คุณเจอ class User, class AccountManager, method ชื่อ Process() และตัวแปร data เต็มไปหมด — ทั้งที่ในทีมของคุณไม่มีใครพูดคำว่า “user” เลย พวกเขาพูดว่า “ผู้กู้” บ้าง “ผู้สมัคร” บ้าง และสองคำนั้น ไม่ใช่สิ่งเดียวกัน
code ที่ได้จึง “ถูก” ในแง่ที่มันรันได้ แต่ “ผิด” ในแง่ที่มันพูดคนละภาษากับทีม และภายในไม่กี่สัปดาห์ ช่องว่างระหว่างคำในหัวคนกับคำใน code ก็กว้างจนไม่มีใครแน่ใจว่า AccountManager.Process() มันทำอะไรกันแน่
บทความนี้เสนอวิธีแก้ที่เรียบง่ายอย่างน่าตกใจ นั่นคือ glossary (บัญชีคำศัพท์ที่นิยามความหมายและบริบทของแต่ละคำ) ของ Ubiquitous Language ควรเป็น context file แรกที่คุณป้อนให้ agent — ป้อนก่อน spec เสมอ ไม่ใช่ป้อนทีหลัง ไม่ใช่ป้อนเมื่อจำเป็น แต่ป้อนเป็นสิ่งแรก
Ubiquitous Language คืออะไร (ทบทวนสั้น ๆ)
หัวข้อที่มีชื่อว่า “Ubiquitous Language คืออะไร (ทบทวนสั้น ๆ)”Ubiquitous Language — เรียกย่อว่า UL หรือ “ภาษากลาง” — คือภาษาที่ “เข้มงวด” ชุดหนึ่งซึ่งทุกคนในทีมใช้ร่วมกัน ทั้งผู้เชี่ยวชาญธุรกิจและนักพัฒนา โดยผูกกับ domain model โดยตรง
อย่างที่บทเรียน Ubiquitous Language ย้ำไว้ว่า ปัญหาคลาสสิกคือผู้เชี่ยวชาญธุรกิจพูดภาษาหนึ่ง นักพัฒนาพูดอีกภาษาหนึ่ง แล้วต้อง “แปล” ไปมา และทุกการแปลคือโอกาสที่ความเข้าใจจะคลาดเคลื่อน
DDD แก้ด้วยการสร้างภาษาเดียวที่ใช้ทั้งในการคุยและใน code — ชื่อ class, method, ตัวแปร ควรสะท้อนคำในภาษานี้
หัวใจอีกข้อคือ ความหมายของคำขึ้นอยู่กับ บริบท คำว่า “meter” (มิเตอร์) ในบริษัทไฟฟ้ามีความหมายต่างกันเล็กน้อยในแต่ละแผนก เราไม่บังคับให้ทุกคนใช้นิยามเดียว แต่ยอมรับว่าความหมายผูกกับ Bounded Context — ขอบเขตเชิงภาษาที่ห่อ UL หนึ่งชุดไว้ ภายในขอบเขตนั้นคำทุกคำมีความหมายเดียว ไม่กำกวม
นี่คือรายละเอียดที่ agent มองข้ามเสมอ และเป็นเหตุผลว่าทำไม glossary ที่ดีต้องระบุ bounded context กำกับทุกคำ
ทีมไทยมีช่องว่างสองชั้น — agent ทำให้เป็นสามชั้น
หัวข้อที่มีชื่อว่า “ทีมไทยมีช่องว่างสองชั้น — agent ทำให้เป็นสามชั้น”ทีมซอฟต์แวร์ไทยจำนวนมากที่ผู้เขียนเคยร่วมงานด้วยอยู่ในสถานการณ์เฉพาะตัว เราคุยงานเป็นภาษาไทย แต่เขียน code เป็นภาษาอังกฤษ ในห้องประชุมมีคนพูดว่า “ยอดค้างชำระ” “การเบิกใช้วงเงิน” “งวดผ่อน” แต่พอเปิด editor คำเหล่านั้นต้องกลายเป็น OutstandingBalance, Drawdown, Installment
การแปลไทย-อังกฤษนี้คือช่องว่างชั้นที่หนึ่งซึ่งมีอยู่แล้วในทุกทีม
เมื่อผู้เชี่ยวชาญธุรกิจกับนักพัฒนายังตีความคำไทยไม่ตรงกัน — เช่น “ลูกค้า” ในมุมของฝ่ายขาย (sales) หมายถึงคนที่กำลังขอสินเชื่อ แต่ในมุมของนักพัฒนา (dev) หมายถึงคนที่ได้เงินกู้ไปแล้ว — นั่นคือช่องว่างชั้นที่สอง สองชั้นนี้คือสิ่งที่ DDD ตั้งใจปิดด้วย UL ตั้งแต่ต้น
ทีนี้พอเราเพิ่ม coding agent เข้ามา มันพก “สมมติฐานเดิม” (priors) ของมันเองมาด้วย — สถิติจาก code นับล้านบรรทัดบนอินเทอร์เน็ตที่บอกว่าอะไรก็ตามที่มีชื่อและยอดเงินควรเรียกว่า User หรือ Customer หรือ Account agent ไม่รู้จัก domain ของคุณ มันเดาจากค่าเฉลี่ยของทั้งโลก
นี่คือช่องว่างชั้นที่สาม และมันร้ายกว่าสองชั้นแรก เพราะ agent เดาได้ “เร็ว” และ “มั่นใจ” — มันจะถม codebase ด้วยคำกลาง ๆ เร็วกว่าที่มนุษย์คนไหนจะถมได้
flowchart TB
subgraph GAP["Without a glossary"]
direction LR
t1["Thai discussion"] --> e1["English code"] --> a1["Agent guesses generic names"]
end
subgraph FIX["With a UL glossary"]
direction LR
g["UL glossary as context"] --> t2["Thai discussion"]
g --> e2["English code"]
g --> a2["Agent uses team terms"]
end
ภาพ 1 — ด้านบน: เมื่อไม่มี glossary เส้นทางจากบทสนทนาไทย สู่ code อังกฤษ สู่ชื่อที่ agent เดาเอง คือช่องว่างสามชั้นที่ต่อกันเป็นทอด ๆ ความเพี้ยนสะสมทีละชั้น · ด้านล่าง: เมื่อมี glossary เป็น context กลาง ทั้งบทสนทนา code และ agent ต่างอ้างอิงคำชุดเดียวกัน ช่องว่างจึงยุบลง glossary ไม่ได้แค่ช่วย agent แต่ทำหน้าที่เป็น “จุดยึด” ให้ทั้งสามฝ่าย
glossary ของ Ubiquitous Language หน้าตาเป็นอย่างไร
หัวข้อที่มีชื่อว่า “glossary ของ Ubiquitous Language หน้าตาเป็นอย่างไร”glossary ที่ป้อนให้ agent ได้จริงไม่ใช่แค่ “คำแปลศัพท์” แต่ละรายการต้องมีอย่างน้อยสามส่วน คือ คำ (term), นิยาม (definition) และ bounded context ที่คำนั้นถือครองความหมายอยู่ ส่วนที่สามนี่แหละที่มักถูกลืม แต่มันคือสิ่งที่แยก glossary ระดับ DDD ออกจากรายการศัพท์ธรรมดา
ยกตัวอย่าง domain สินเชื่อ (lending) ที่เราจะใช้ตลอดบทความ ระบบแบ่งเป็น2 bounded context ได้แก่ Origination (การพิจารณาและอนุมัติสินเชื่อ) และ Servicing (การดูแลบัญชีสินเชื่อหลังปล่อยกู้) glossary ย่อ ๆ อาจเป็นดังนี้
| Term | ไทย | นิยาม | Bounded Context |
|---|---|---|---|
| Applicant | ผู้สมัคร | ผู้ยื่นขอสินเชื่อที่ยังไม่ได้รับอนุมัติหรือยังไม่ได้เบิกเงิน | Origination |
| Application | ใบคำขอ | คำขอสินเชื่อหนึ่งใบ พร้อมเอกสารและผลการพิจารณา | Origination |
| CreditLimit | วงเงิน | จำนวนเงินสูงสุดที่อนุมัติให้ผู้สมัครรายหนึ่ง | Origination |
| Borrower | ผู้กู้ | ผู้สมัครที่ได้รับการเบิกจ่ายเงินกู้แล้ว | Servicing |
| LoanAccount | บัญชีสินเชื่อ | บัญชีสินเชื่อที่เปิดใช้งานของผู้กู้หนึ่งราย | Servicing |
| Drawdown | การเบิกใช้วงเงิน | การเบิกเงินจากวงเงินแต่ละครั้ง | Servicing |
| OutstandingBalance | ยอดเงินต้นคงค้าง | เงินต้นที่ยังไม่ได้ชำระ ณ เวลาปัจจุบัน | Servicing |
| Delinquency | การผิดนัดชำระ | สถานะที่งวดผ่อนเลยกำหนดชำระแล้ว | Servicing |
สังเกตว่า column Bounded Context ไม่ได้ตกแต่งให้สวย มันคือ กติกาการตั้งชื่อ ตรง ๆ คำว่า CreditLimit เป็นเรื่องของ Origination เท่านั้น ส่วน OutstandingBalance อยู่ฝั่ง Servicing การเอาสองคำนี้มาปนกันใน class เดียวคือสัญญาณว่าเรากำลังทำ bounded context รั่ว ซึ่ง agent จะทำโดยไม่รู้ตัวถ้าเราไม่บอก
คำเดียว สองบริบท: Applicant กับ Borrower
หัวข้อที่มีชื่อว่า “คำเดียว สองบริบท: Applicant กับ Borrower”จุดที่ glossary แสดงพลังชัดที่สุดคือคำหรือแนวคิดเดียวที่มีความหมายต่างกันไปตามบริบท (polyseme) ใน domain สินเชื่อ “คน ๆ เดียวกัน” ที่เดินเข้ามาขอกู้ มีชื่อไม่เหมือนกันในสองบริบท ก่อนเงินออกเขาคือ Applicant (ผู้สมัคร) หลังเงินออกแล้วเขากลายเป็น Borrower (ผู้กู้)
มันไม่ใช่แค่เปลี่ยนคำเล่น ๆ — Applicant มี Application และ CreditLimit ส่วน Borrower มี LoanAccount และ OutstandingBalance 2 entity นี้มีกฎ (invariant) คนละชุด มี life cycle คนละแบบ
flowchart TB
person["Same real person"]
subgraph ORIG["Origination context"]
applicant["Applicant"]
end
subgraph SERV["Servicing context"]
borrower["Borrower"]
end
person --> applicant
person --> borrower
applicant -->|after disbursement| borrower
ภาพ 2 — บุคคลจริงคนเดียวกันปรากฏเป็นคนละแนวคิดในคนละ bounded context ในบริบท Origination เขาคือ Applicant ในบริบท Servicing เขาคือ Borrower เส้นเชื่อม “after disbursement” คือเหตุการณ์เปลี่ยนสถานะเมื่อเงินกู้ถูกเบิกจ่าย นี่คือสิ่งที่ agent จะยุบให้เหลือ User ตัวเดียวเสมอถ้าไม่มี glossary กำกับ — และเมื่อยุบแล้ว กฎธุรกิจของสองสถานะก็จะปนกันจนแยกไม่ออก
agent ที่ไม่มี context จะสร้าง class User ก้อนเดียวที่มีทั้ง CreditLimit และ OutstandingBalance อยู่ด้วยกัน ซึ่งเท่ากับลบเส้นแบ่ง bounded context ทิ้งตั้งแต่บรรทัดแรก glossary ที่ระบุบริบทกำกับจึงไม่ใช่เอกสารประดับ แต่คือการบังคับให้ agent เคารพขอบเขตที่ทีมออกแบบไว้
ทำไมต้อง glossary “ก่อน” spec
หัวข้อที่มีชื่อว่า “ทำไมต้อง glossary “ก่อน” spec”ประเด็นสำคัญของบทความนี้ไม่ใช่แค่ “ควรมี glossary” แต่คือ ลำดับ spec ทุกฉบับตั้งอยู่บนข้อสมมติแฝงว่าผู้อ่านรู้อยู่แล้วว่าคำต่าง ๆ หมายถึงอะไร
เวลาเราเขียน spec ว่า “เมื่อผู้กู้เบิกใช้วงเงินเกินยอดคงเหลือ ให้ปฏิเสธรายการ” ประโยคนี้จะมีความหมายก็ต่อเมื่อผู้อ่านรู้แล้วว่า “ผู้กู้” “เบิกใช้วงเงิน” และ “ยอดคงเหลือ” คืออะไร
spec คือชั้นของ “ทำอะไร” ส่วน glossary คือชั้นของ “เราเรียกสิ่งต่าง ๆ ว่าอะไร” — และชั้นหลังอยู่ ใต้ ชั้นแรกเสมอ
ถ้าคุณป้อน spec โดยไม่ป้อน glossary ก่อน agent จะเติมช่องว่างของคำศัพท์ด้วยสมมติฐานเดิมของมันเอง แล้วค่อยทำตาม spec บนคำที่มันเดาขึ้นมา ผลคือ code ทำสิ่งที่ spec บอก แต่ใช้คำที่ทีมไม่ยอมรับ ในทางกลับกัน ถ้า glossary มาก่อนเสมอ ตัว spec เองก็จะถูกตีความบนคำศัพท์ชุดที่ถูกต้องตั้งแต่ต้น
flowchart LR gloss["UL Glossary"] --> spec["Spec"] spec --> code["Generated Code"] gloss -.->|shared vocabulary| code
ภาพ 3 — glossary อยู่ต้นน้ำของ spec ไม่ใช่ข้าง ๆ spec ถูกเขียนและตีความบนคำศัพท์ที่ glossary นิยามไว้ และคำศัพท์ชุดเดียวกันนั้นก็ไหลตรงลงไปถึง code ที่ generate ออกมา (เส้นประ) เมื่อคำต้นทางถูกล็อกไว้ ความกำกวมจะไม่ถูกเลื่อนไปสะสมที่ปลายน้ำ
นี่คล้ายกับหลักการ spec-anchored ในบทความ Spec-Driven Development แต่ไม่ใช่สิ่งเดียวกันเสียทีเดียว — spec-anchored ในบทความนั้นหมายถึงการ “คง spec ไว้ใช้ดูแล/พัฒนาต่อ” คือเก็บ spec ไว้เป็นชิ้นงานที่มีชีวิตข้ามรอบการดูแลรักษา ส่วนสิ่งที่บทความนี้พูดถึงคือความมั่นคงของคำศัพท์ที่ spec อิงอยู่
ทั้งสองอย่างเสริมกัน spec จะเป็นจุดยึดที่มั่นคงได้ก็ต่อเมื่อมันตั้งอยู่บนคำศัพท์ที่มั่นคงกว่า glossary จึงเป็น “จุดยึดของจุดยึด” ที่อยู่ใต้ spec ลงไปอีกชั้น
แปลง glossary เป็น CLAUDE.md
หัวข้อที่มีชื่อว่า “แปลง glossary เป็น CLAUDE.md”ทีนี้มาถึงส่วนที่ปฏิบัติได้จริง glossary ในหัวหรือใน Confluence ไม่ช่วยอะไร agent เลย มันต้องอยู่ในที่ที่ agent อ่านทุกครั้ง นั่นคือ file context ประจำ project เช่น CLAUDE.md หรือส่วนหนึ่งของ system prompt
วิธีที่ได้ผลคือแปลงตาราง glossary ให้เป็น block ที่มี กติกาการตั้งชื่อ และ คำต้องห้าม ชัดเจน ไม่ใช่แค่รายการนิยาม
## Ubiquitous Language (อ่านก่อนเขียน code เสมอ)
กติกา: ตั้งชื่อ class, method, ตัวแปร ตามคำในหมวดนี้เท่านั้นห้ามใช้คำกลาง ๆ อย่าง User, Data, Manager, Info, Handler แทนคำใน domainทุกคำผูกกับ bounded context หนึ่งเดียว ห้ามข้ามบริบท
### Bounded Context: Origination (การพิจารณาสินเชื่อ)- Applicant — ผู้ยื่นขอสินเชื่อที่ยังไม่ได้เบิกเงิน- Application — ใบคำขอสินเชื่อหนึ่งใบ- CreditLimit — วงเงินสูงสุดที่อนุมัติให้
### Bounded Context: Servicing (การดูแลบัญชีสินเชื่อ)- Borrower — Applicant ที่ได้รับการเบิกจ่ายเงินกู้แล้ว- LoanAccount — บัญชีสินเชื่อที่เปิดใช้งาน- Drawdown — การเบิกใช้วงเงินแต่ละครั้ง- OutstandingBalance — ยอดเงินต้นคงค้าง ณ ปัจจุบัน
### คำต้องห้าม (ถ้าจะใช้ ให้หยุดแล้วถามก่อน)- User, Customer → ต้องระบุว่าเป็น Applicant หรือ Borrower- Account → ต้องระบุว่าเป็น Application หรือ LoanAccount- Manager, Service ลอย ๆ → ตั้งชื่อตามพฤติกรรมใน domain แทนเคล็ดลับสามข้อที่ทำให้ block นี้ได้ผลกับ agent จริง
- หนึ่ง — จัดกลุ่มคำใต้หัวข้อ bounded context เสมอ เพื่อให้ agent เห็นเส้นแบ่งบริบทเป็นโครงสร้าง ไม่ใช่แค่รายการยาว ๆ
- สอง — มีหมวด “คำต้องห้าม” ที่บอกตรง ๆ ว่าเจอคำกลาง ๆ แล้วให้ทำอย่างไร เพราะการห้ามเชิงรุกได้ผลกว่าการบอกแต่สิ่งที่ควรทำ
- สาม — สั่งให้ “หยุดแล้วถามก่อน” เมื่อไม่มีคำที่ตรง แทนที่จะปล่อยให้ agent คิดคำใหม่ขึ้นเอง เพราะคำใหม่ที่ไม่ผ่านทีมคือจุดเริ่มของการเพี้ยนรอบใหม่
ก่อนและหลัง: code ที่ agent generate
หัวข้อที่มีชื่อว่า “ก่อนและหลัง: code ที่ agent generate”ต่อไปนี้คือความต่างที่จับต้องได้ สมมติเราขอให้ agent implement การบันทึกการเบิกใช้วงเงิน code ที่ agent มักเขียน เมื่อไม่มี glossary จะหน้าตาประมาณนี้
// สิ่งที่ agent เดาเอาเองเมื่อไม่มี context — ชื่อกลาง ๆ ไร้บริบทpublic class User{ public decimal Balance { get; set; } // Balance ของอะไร วงเงิน หรือ ยอดค้าง}
public class AccountManager{ // Process อะไร ใครอ่านก็ต้องไปเดาจาก implementation public void Process(User user, int amount) { user.Balance += amount; }}ปัญหาไม่ใช่ว่ามันรันไม่ได้ ปัญหาคือทุกชื่อในนี้บังคับให้คนอ่านต้องเดา Balance เป็นวงเงินหรือยอดค้าง Process ทำอะไร User คือ Applicant หรือ Borrower และเพราะทุกอย่างถูกยัดใน User ก้อนเดียว เส้นแบ่ง bounded context ก็หายไปแล้ว
code ที่ agent เขียน เมื่อป้อน glossary ก่อน จะพูดภาษาเดียวกับทีม
// Servicing context — ทุกชื่อมาจาก Ubiquitous Language ของทีมpublic sealed class LoanAccount{ public Money OutstandingBalance { get; private set; }
// "การเบิกใช้วงเงิน" = RecordDrawdown ไม่ใช่ "add amount" public void RecordDrawdown(Drawdown drawdown) { OutstandingBalance = OutstandingBalance.Add(drawdown.Principal); }}อ่าน method RecordDrawdown แล้วออกเสียงเป็นประโยคธุรกิจได้เลยว่า “บัญชีสินเชื่อบันทึกการเบิกใช้วงเงิน” — นั่นคือสัญญาณของ UL ที่ดีตามที่บทเรียนในคอร์สอธิบายไว้
และที่สำคัญ LoanAccount ไม่มี CreditLimit ปนเข้ามา เพราะ glossary บอกชัดว่าคำนั้นอยู่ฝั่ง Origination agent จึงเคารพขอบเขตโดยไม่ต้องมีใครมารีวิวจับทีหลัง
glossary อยู่ตรงไหนใน SDD และ SPDD
หัวข้อที่มีชื่อว่า “glossary อยู่ตรงไหนใน SDD และ SPDD”ถ้าคุณอ่านบทความ Structured-Prompt-Driven Development (SPDD) มาแล้ว จะจำ REASONS Canvas ได้ ซึ่งมีช่อง E — Entities (entity ของ domain และความสัมพันธ์) และ N — Norms (บรรทัดฐานทางวิศวกรรม เช่น การตั้งชื่อ)
glossary ของ UL คือ “วัตถุดิบ” ที่เติมสองช่องนั้นได้ตรงที่สุด เพราะมันคือรายการ entity พร้อมบริบท และคือกติกาการตั้งชื่อในตัวเดียวกัน พูดอีกอย่างคือ SPDD มีช่องรอไว้ให้ UL อยู่แล้ว เราแค่ต้องเตรียม glossary ให้พร้อมก่อนจะเริ่มกรอก Canvas
ในมุมของ SDD ที่ยึด spec เป็นหลักในการทำงาน glossary คือชั้นที่อยู่ใต้ spec ลงไปอีกชั้น spec บอกว่าระบบควรทำอะไร glossary บอกว่าคำที่ spec ใช้หมายถึงอะไรและอยู่บริบทไหน ทั้งสองแนวทางจึงไม่ได้แข่งกับ glossary แต่ต้องการมันเป็นฐาน
ลำดับที่แนะนำสำหรับทีมที่ทำงานกับ agent จึงเป็น glossary → spec/prompt → code โดยมี glossary เป็น file context ที่โหลดอยู่ตลอดทุกขั้น ไม่ใช่ป้อนครั้งเดียวแล้วลืม
glossary ที่มีชีวิต: sync ให้ทัน model
หัวข้อที่มีชื่อว่า “glossary ที่มีชีวิต: sync ให้ทัน model”glossary ไม่ใช่หินสลัก Evans ย้ำว่า “การเปลี่ยนภาษาคือการเปลี่ยน model” เมื่อทีมค้นพบว่าคำเดิมสื่อความไม่ตรง หรือพบแนวคิดใหม่ที่ยังไม่มีชื่อ นั่นคือสัญญาณให้ปรับ glossary — และเมื่อ glossary เปลี่ยน file context ที่ป้อน agent ก็ต้องเปลี่ยนตาม
นี่คือวินัย sync แบบเดียวกับที่ SPDD ใช้กับ prompt กับ code เพียงแต่ขยับขึ้นไปอีกชั้นหนึ่งคือชั้นของคำศัพท์
flowchart LR model["Domain model"] --> gloss["UL glossary"] gloss --> prompt["CLAUDE.md context"] prompt --> code["Generated code"] code -.->|new term surfaced| gloss gloss -.->|sharper language| model
ภาพ 4 — วงรอบของ glossary ที่มีชีวิต domain model หล่อเลี้ยง glossary, glossary กลายเป็น context ใน CLAUDE.md, context นำทาง code ที่ generate ออกมา และเมื่อระหว่างเขียน code มีคำใหม่โผล่ขึ้น (เส้นประล่าง) คำนั้นถูกป้อนกลับเข้า glossary ซึ่งในทางกลับกันก็ทำให้ domain model คมขึ้น ทั้งวงต้องหมุนไปด้วยกัน ไม่ใช่แช่แข็ง glossary ไว้ตั้งแต่วันแรก
ในทางปฏิบัติ ให้ถือว่า CLAUDE.md ส่วน Ubiquitous Language เป็น file ที่ผ่าน code review เหมือน code ทั่วไป ทุกครั้งที่มีคำใหม่เข้า PR หรือคำเดิมเปลี่ยนความหมาย ให้แก้ที่ glossary ก่อน แล้วค่อยให้ agent generate code รอบใหม่บน glossary ที่อัปเดตแล้ว
วิธีนี้ทำให้คำศัพท์ของทีมกับคำใน code ไม่มีวันเบนออกจากกันแบบเงียบ ๆ
สรุป: เช็ก list ก่อนป้อน spec ตัวถัดไป
หัวข้อที่มีชื่อว่า “สรุป: เช็ก list ก่อนป้อน spec ตัวถัดไป”ก่อนจะเปิด agent แล้วโยน spec ให้มันในครั้งหน้า ลองไล่เช็ก list สั้น ๆ นี้ก่อน
- มี glossary ที่ทุกรายการมีครบสามส่วน — term, นิยาม, และ bounded context — หรือยัง
- glossary อยู่ใน file context ที่ agent อ่านทุกครั้ง (เช่น
CLAUDE.md) หรือยังค้างอยู่ใน Confluence - มีหมวด คำต้องห้าม ที่บอก agent ว่าเจอ
User/Account/Managerลอย ๆ แล้วให้หยุดถามหรือยัง - คำที่เป็น polyseme (เช่น Applicant กับ Borrower) ถูกแยกตามบริบทชัดเจนไหม
- glossary ถูกป้อน ก่อน spec ไม่ใช่หลัง
- มีวินัย sync ที่แก้ glossary ก่อนแก้ code เมื่อภาษาของทีมเปลี่ยนหรือยัง
ทีมไทยที่คุยงานเป็นไทยแต่ code เป็นอังกฤษมีช่องว่างสองชั้นอยู่แล้ว และ agent พร้อมจะเปลี่ยนมันเป็นสามชั้นด้วยความเร็วที่มนุษย์ตามไม่ทัน glossary ของ Ubiquitous Language คือเครื่องมือที่ปิดช่องว่างทั้งสามชั้นพร้อมกัน — ขอเพียงคุณป้อนมันเป็นสิ่งแรก ก่อน spec เสมอ
📄 อ่านต่อในไซต์:
- Ubiquitous Language — คอร์ส DDD — ที่มาของแนวคิดภาษากลาง พร้อมตัวอย่าง Cargo Shipping
- Ubiquitous Language — DevIQ และ Bounded Context — DevIQ — หน้าอ้างอิงสั้นของสองแนวคิดหลักในบทความนี้
- Domain Model — DevIQ และ Context Map — DevIQ — โครงที่ glossary ผูกอยู่และแผนที่ความสัมพันธ์ระหว่างบริบท
- Spec-Driven Development และ Structured-Prompt-Driven Development (SPDD) — สองแนวทางที่ glossary เป็นฐานให้