Software Engineer

เขียน AI Context Files ด้วย Information Architecture

เอาหลัก Information Architecture และ Information Hierarchy มาจัดโครงสร้าง AI context files ให้ทุก vendor ใช้ร่วมกันได้ แต่ละเรื่องมีไฟล์เจ้าของไฟล์เดียว (SSOT) ไม่เขียนซ้ำ (DRY) และอ้างอิงทางเดียว (DAG)

Intro

หลายๆ คนที่เริ่มใช้ AI coding agent ในทีม น่าจะเคยเจอปัญหาคล้ายๆ กัน คือ Dev แต่ละคนถนัดคนละเจ้า บางคนใช้ Claude Code บางคนใช้ GitHub Copilot บางคนใช้ Codex หรือ Gemini CLI และแต่ละเจ้าก็มีไฟล์ context ของตัวเอง CLAUDE.md, .github/copilot-instructions.md, .claude/rules/, .github/instructions/, .codex/ และอื่นๆ อีกมาก

ทีมผมตั้งใจไว้ตั้งแต่แรกว่าจะไม่ผูกกับ AI เจ้าใดเจ้าหนึ่ง ใครถนัดตัวไหนก็ใช้ตัวนั้น แต่สิ่งที่ต้องได้เหมือนกันคือ agent ทุกตัวต้องเข้าใจระบบไปในทางเดียวกัน ใช้กฎชุดเดียวกัน และ build/test ด้วยคำสั่งเดียวกัน

พอทำไปสักพัก ไฟล์ context ก็เยอะขึ้นเรื่อยๆ (repo ที่ผมดูแลมี AI context file เกือบ 300 ไฟล์ ถ้านับ copy ของแต่ละ vendor ด้วย) สิ่งที่ผมอยากได้จากไฟล์ชุดนี้มีสามข้อ

  1. SSOT (Single Source of Truth) แต่ละเรื่องมีไฟล์เจ้าของไฟล์เดียว ถ้าสองที่พูดไม่ตรงกัน ให้ถือไฟล์เจ้าของเป็นหลัก
  2. DRY (Don't Repeat Yourself) ไฟล์อื่นที่ต้องใช้เรื่องนั้นชี้ไปหาไฟล์เจ้าของ ไม่เขียนซ้ำ แก้ที่เดียวแล้ว agent ทุกตัวเห็นเหมือนกัน
  3. DAG (Directed Acyclic Graph) อ้างอิงทางเดียว agent อ่านจากแผนที่ลงไปหารายละเอียด โดยไม่ต้องวนกลับมาอ่านไฟล์เดิม

ใน Blog นี้จะเล่าว่าผมเอาหลักของ Information Architecture (IA) กับ Information Hierarchy ซึ่งเป็นเรื่องของฝั่ง UX/Technical Writing มาจัดโครงสร้างไฟล์เหล่านี้ยังไง โดยจะไม่ลงรายละเอียดว่า vendor ไหน config ยังไง เพราะเปลี่ยนเร็วมาก

ถ้าอ่านแค่ส่วนนี้ ข้อสรุปมีสามข้อ

  • หนึ่งเรื่อง มีไฟล์เจ้าของไฟล์เดียว (SSOT + DRY) ไฟล์อื่นชี้ไปหา ไม่อธิบายซ้ำ
  • อ้างอิงทางเดียว (DAG) จากไฟล์แผนที่ลงไปหาไฟล์รายละเอียด ไม่ชี้ย้อนกลับ และไม่ชี้หากันเองในชั้นเดียวกัน
  • Vendor file เป็นแค่ adapter มี source ที่เดียว แล้วใช้ script sync ออกไปให้แต่ละเจ้า

Pain Point

ตัวอย่างที่เจอจริงใน repo ผมเอง คือไฟล์ readme.md ใน .claude/skills/ ซึ่งเคยเขียนกฎไว้สองข้อ ข้อแรกบอกว่าเรามีทั้ง .gemini/skills/ และ .claude/skills/ ทั้งที่ .gemini/skills/ ไม่เคยมีอยู่ใน repo เลย ข้อสองสั่งว่าห้ามใช้ .github/skills/ เพราะเข้าใจว่า Copilot อ่าน skill จากโฟลเดอร์ของ Claude Code แต่จริงๆ Copilot อ่านได้ทั้ง .github/skills/, .claude/skills/ และ .agents/skills/ และ .github/skills/ ก็คือโฟลเดอร์หลักของ Copilot เอง กฎสองข้อนี้อยู่ใน repo หลายเดือนกว่าจะมีคนเจอ

ที่น่ากลัวคือ ถ้า agent อ่านเจอกฎแบบนี้ มันจะทำตามอย่างมั่นใจ โดยไม่มีอะไร fail ให้เห็นเลย เอกสารสำหรับคน ถ้าผิด คนอ่านยังอาจจะสงสัย แต่ agent ไม่สงสัย

IA กับ Information Hierarchy ต่างกันยังไง

สองคำนี้มักใช้ปนกัน แต่ตอบคนละคำถาม

Aspect Information Architecture Information Hierarchy
Key question ข้อมูลอยู่ที่ไหน และเชื่อมกันยังไง อะไรสำคัญกว่า และควรเห็นก่อน
Scope ระดับระบบ หลายไฟล์ หลายโฟลเดอร์ ภายในไฟล์เดียว
Analogy ผังห้องในบ้าน การจัดของในแต่ละห้อง ของใช้บ่อยวางไว้หน้าสุด
Information Architecture ข้อมูลอยู่ที่ไหน และเชื่อมกันยังไง โถงทางเข้า AGENTS.md docs/System ระบบคืออะไร instructions กฎตามชนิดไฟล์ skills ขั้นตอนงาน Information Hierarchy อะไรสำคัญกว่า และควรเห็นก่อน ข้อสรุป / กฎสำคัญ เหตุผลหลัก เหตุผลหลัก อ่านก่อน ลงลึกทีหลัง
IA คือผังห้องว่าอะไรอยู่ที่ไหน ส่วน Hierarchy คือการจัดของในห้องว่าอะไรต้องเห็นก่อน

IA แบ่งได้เป็นสี่ระบบ คือ Organization (จัดกลุ่ม), Labeling (ตั้งชื่อ), Navigation (ทางไปต่อ) และ Search (หาเจอ) ซึ่งพอเอามาเทียบกับ AI context file ก็ตรงตัวมาก

  • Organization คือโครงสร้างโฟลเดอร์ docs/, .agents/instructions/, .agents/skills/
  • Labeling คือชื่อไฟล์ และ description ใน frontmatter ที่ agent ใช้ตัดสินใจว่าจะโหลดไฟล์ไหน
  • Navigation คือ link และตารางใน AGENTS.md
  • Search คือ agent ที่ grep หาเองได้อยู่แล้ว ถ้าตั้งชื่อดี
OrganizationLabelingNavigationSearch จัดกลุ่มตั้งชื่อทางไปต่อหาเจอ โครงสร้างโฟลเดอร์ docs/ .agents/ ชื่อไฟล์ และ description ใน frontmatter link และตาราง "ถ้าจะทำ X อ่าน Y" ใน AGENTS.md agent grep หาเอง ได้อยู่แล้ว ถ้าตั้งชื่อดี
ระบบทั้งสี่ของ IA เมื่อเอามาเทียบกับ AI context files

ถ้า IA ไม่ดี agent หาไม่เจอ ถ้า Hierarchy ไม่ดี agent เจอแล้วแต่ไม่รู้ว่าอะไรสำคัญ

หลักที่หยิบมาใช้

  • Pyramid Principle / BLUF ขึ้นต้นด้วยข้อสรุป แล้วค่อยตามด้วยเหตุผลและรายละเอียด ถ้าอ่านแค่บรรทัดแรกก็ต้องได้ประเด็นแล้ว
  • MECE แบ่งหัวข้อไม่ให้ซ้อนกันและไม่ให้ขาด ข้อนี้ทำให้ได้ DRY ตั้งแต่ต้นทาง เพราะแต่ละเรื่องมีที่อยู่ที่เดียว
  • Progressive Disclosure ชั้นบนเป็นแผนที่ ชั้นล่างเป็นรายละเอียด ผู้อ่านเลือกเองว่าจะลงลึกแค่ไหน
  • Chunking แบ่งเนื้อหาเป็นก้อนเล็กๆ ถ้าหัวข้อหนึ่งมีเกิน 4–5 ข้อ ให้จัดเป็นกลุ่มย่อย

ทำไมใช้กับ AI ได้ (และได้ผลกว่าใช้กับคนด้วย)

LLM มีข้อจำกัดแบบเดียวกับคน แต่ชัดกว่า

  • Context คืองบประมาณความสนใจ เป้าหมายคือใส่ token ให้น้อยที่สุดแต่มีสาระมากที่สุด ข้อความซ้ำหรือข้อความที่ไม่เกี่ยวกับงานจะไปลดน้ำหนักของข้อความที่สำคัญ
  • Lost in the Middle model ใช้ข้อมูลที่อยู่ต้นและท้าย context ได้ดีกว่าข้อมูลที่อยู่ตรงกลาง กฎสำคัญจึงควรอยู่ต้นไฟล์
  • Context Rot ยิ่ง input ยาว ผลลัพธ์ยิ่งไม่น่าเชื่อถือ แม้จะเป็นงานง่ายๆ ก็ตาม
  • Agent โหลดไฟล์ตามที่ต้องใช้ เช่น Agent Skills ที่โหลดแค่ name กับ description ตอนเริ่ม แล้วค่อยโหลด SKILL.md ตัวเต็มเมื่องานตรง และโหลดไฟล์ใน references/ เมื่อ SKILL.md ชี้ไปเท่านั้น นี่ก็คือ Progressive Disclosure ตรงๆ เลย

จะเห็นได้ว่าการเขียน context ให้ AI ไม่ได้ต่างจากการทำ IA ให้คนมากนัก แค่ผู้อ่านเป็น agent ที่อ่านเร็วมาก ไม่สงสัย และลืมทุกอย่างเมื่อจบ session

โครงสร้างที่ใช้: Source ที่เดียว, Adapter หลายเจ้า

ปัญหาแรกที่ต้องแก้คือเรื่อง vendor neutral เพราะแต่ละเจ้าอ่านไฟล์คนละที่

  • AGENTS.md เป็น open standard ที่หลายเจ้าอ่านได้
  • Claude Code อ่าน CLAUDE.md เป็นหลัก (เวอร์ชันใหม่จะอ่าน AGENTS.md แทนถ้าไม่มี CLAUDE.md)
  • Copilot อ่าน .github/copilot-instructions.md กับ .github/instructions/*.instructions.md ที่มี applyTo
  • Claude Code อ่าน .claude/rules/ ที่ใช้ paths แทน applyTo

ผมเลยแยกเป็น source ที่แก้ได้ที่เดียว กับ adapter ที่ห้ามแก้ตรงๆ

Source: แก้ที่นี่ที่เดียว AGENTS.md แผนที่ L0 .agents/ instructions/skills/agents/ กฎ, skills, specialist agents sync script ที่แต่ละ vendor อ่าน Claude Code CLAUDE.md → @AGENTS.md .claude/rules/, skills/ GitHub Copilot copilot-instructions.md .github/instructions/, skills/ Codex AGENTS.md (อ่านตรง) .codex/agents/*.toml adapter: ไฟล์สั้นๆ ที่ชี้ไป AGENTS.md generated copy: ห้ามแก้ตรงๆ
แก้แค่ฝั่งซ้าย ฝั่งขวาเป็น adapter หรือ copy ที่ script สร้างให้
AGENTS.md                         ← L0 แผนที่ (source)
CLAUDE.md                         ← adapter: มีบรรทัดเดียวคือ @AGENTS.md
.github/copilot-instructions.md   ← adapter: บอกแค่ว่าให้ไปอ่าน AGENTS.md
.agents/                          ← source ของ skills, instructions, agents
├── instructions/
├── skills/
└── agents/
.claude/rules/, .claude/skills/   ← generated copy (sync จาก .agents/)
.github/instructions/, skills/    ← generated copy
.codex/agents/*.toml              ← generated (แปลงเป็น TOML)

ตัว CLAUDE.md มีแค่นี้จริงๆ

@AGENTS.md

ส่วน copy ใน .claude/ กับ .github/ ใช้ script sync ออกไป โดยตอน sync ไป .claude/rules/ script จะแปลง applyTo เป็น paths และเปลี่ยนชื่อไฟล์จาก <name>.instructions.md เป็น <name>.md ให้ด้วย และมีอีกคำสั่งที่แค่ report ว่า copy ไหน drift จาก source บ้าง

ดูแบบนี้อาจจะมีคนสงสัยว่าก็ยังซ้ำกันอยู่ดี ซึ่งก็ซ้ำจริงครับ แต่เป็นการซ้ำแบบ generated ซึ่งยังถือว่า DRY เพราะมีที่ให้แก้แค่ที่เดียว คิดง่ายๆ ว่าเหมือน .cs กับ .dll ไม่มีใครถือว่า .dll เป็น copy ที่ต้อง maintain เอง

DRY สำหรับ AI context หมายถึง แต่ละเรื่องมีที่ให้แก้ที่เดียว ส่วน generated copy หรือ pointer บรรทัดเดียวยังอยู่ในหลักนี้

เรื่อง precedence ต้องอยู่ที่ L0

อีกเรื่องที่เจอคือ ถ้า instruction สองไฟล์ match กับไฟล์เดียวกันแล้วขัดกัน ใครชนะ? คำตอบคือไม่มี harness ไหนตัดสินให้ ตอนรวม instruction หลายไฟล์เข้า context ไม่มีการรับประกันลำดับ

ผมเลยเขียนกฎ precedence ไว้ใน AGENTS.md ที่เดียว เพราะเป็นไฟล์ที่ถูกโหลดทุก request แน่ๆ และห้ามเขียนเรื่องนี้ไว้ในตัว instruction file เอง

Layer ของ Context

พอแยก source กับ adapter แล้ว ต่อมาคือจัด Hierarchy ของ source

Layer ไฟล์ หน้าที่
L-1 Tooling .editorconfig, analyzers, build, tests กฎที่เครื่องตรวจได้
L0 Map AGENTS.md, CONSTITUTION.md แผนที่ + หลักการที่ห้ามละเมิด โหลดทุก request
L1 System docs/System/ARCHITECTURE.md, ONBOARDING.md ระบบคืออะไร ประกอบด้วยอะไร ตั้งเครื่องยังไง
L2 Detail .agents/instructions/*, .agents/skills/*, README.md ของแต่ละ package รายละเอียดเฉพาะเรื่อง โหลดเมื่อตรงงาน
link ชี้ลงอย่างเดียว L0 Map AGENTS.md, CONSTITUTION.md L1 System ARCHITECTURE.md, ONBOARDING.md L2 Detail instructions, skills, package README L-1 Tooling .editorconfig, analyzers, build, tests โหลดทุก requestอ่านเมื่อต้องเข้าใจระบบโหลดเมื่องานตรงเครื่องตรวจให้ สั้น เป็นแผนที่ระบบคืออะไรรายละเอียดเฉพาะเรื่องไม่ต้องเขียนเป็น prose
ยิ่งอยู่ชั้นบน ยิ่งสั้นและถูกโหลดบ่อย ชั้นล่างสุดเป็นกฎที่เครื่องตรวจได้

Layer ที่ผมเพิ่มเองคือ L-1 Tooling ซึ่งอยู่ต่ำกว่าเอกสารทั้งหมด หลักคือ

กฎไหนที่ tool ตรวจได้ ไม่ควรอยู่ใน prose

ตัวอย่างเช่น copyright header ของทุกไฟล์ หรือการบังคับให้ public member ต้องมี documentation comment เคยเขียนไว้ในเอกสาร แต่ตอนนี้อยู่ใน .editorconfig อย่างเดียว เพราะ analyzer บังคับได้แน่นอนกว่า prose และ prose ที่เขียนซ้ำก็คือ source ที่สองที่รอวัน drift

อีกเรื่องที่ช่วยเยอะคือ ใน L0 ให้ใช้ตาราง "ถ้าจะทำ X ให้อ่าน Y" แทนการอธิบาย เช่น

| Task                 | Read                                                    |
| -------------------- | ------------------------------------------------------- |
| C# code              | csharp-code.instructions.md, csharp-docs.instructions.md |
| Database migration   | ef-migrations.instructions.md                           |
| Dockerfile / Compose | docker.instructions.md                                  |
| CI pipeline          | pipelines.instructions.md                               |

ตารางนี้ทำหน้าที่ทั้ง Navigation และ Labeling ในคราวเดียว agent ไม่ต้องเปิดทุกไฟล์เพื่อดูว่าไฟล์ไหนเกี่ยว

SSOT + DRY: หนึ่งเรื่อง หนึ่งเจ้าของ

สองหลักนี้มาคู่กัน SSOT บอกว่าความจริงของแต่ละเรื่องอยู่ที่ไฟล์ไหน ส่วน DRY บอกว่าไฟล์อื่นไม่ต้องเขียนเรื่องนั้นซ้ำ ให้ชี้ไปหาไฟล์เจ้าของแทน

ก่อน: สองที่ที่ต้องแก้พร้อมกัน a.md Rule X = v2 แก้แล้ว b.md Rule X = v1 ลืมแก้ agent อ่านเจอทั้งสองไฟล์ แล้วจะเชื่อไฟล์ไหน? SSOT + DRY: เจ้าของเดียว + pointer rules.md (owner) Rule X = v2 a.md Rule X: ดู rules.md b.md Rule X: ดู rules.md
ขวาคือ DRY หนึ่ง fact มีเจ้าของไฟล์เดียว ไฟล์อื่นชี้ไปหา แก้ที่เดียวก็ถูกทุกที่

กฎที่ทีมใช้จริง

  1. หนึ่ง fact อยู่ในไฟล์เจ้าของไฟล์เดียว (SSOT) ไฟล์อื่นที่ต้องใช้ให้ link ไปหา (DRY)
  2. กฎที่ใช้กับทั้งชุดอยู่ในไฟล์แม่ ไฟล์ลูกเก็บแค่สิ่งที่เฉพาะกับตัวเอง เพราะไฟล์ลูกถูกอ่านใน context ของไฟล์แม่อยู่แล้ว กฎที่เขียนซ้ำจะถูกอ่านสองรอบ และพอแก้ครั้งแรกก็จะพูดไม่เหมือนกัน
  3. Pointer มีบรรทัดเดียว บอกชื่อไฟล์และว่าไฟล์นั้นครอบคลุมอะไร เช่น "references/error-messages.md: how the error message is chosen" ถ้า pointer เริ่มเล่ากฎของไฟล์ลูก ก็กลายเป็น copy แล้ว
  4. ย้ายแล้วต้องลบของเก่าใน commit เดียวกัน ถ้าปล่อยไว้ทั้งสองที่ ก็เป็น copy ตั้งแต่วินาทีที่ merge
  5. อย่า copy โค้ดลงเอกสาร ชี้ไปที่ไฟล์โค้ดแทน เอกสารควรบอก why กับขอบเขต ส่วน how ให้โค้ดบอก

ข้อยกเว้นที่ผมยอมให้ซ้ำคือ orientation copy เช่นส่วน Boundaries ใน AGENTS.md ที่บอกว่า path ไหนห้ามแก้ ซึ่งรายละเอียดจริงอยู่ใน ARCHITECTURE.md แต่ต้องเห็นตั้งแต่ L0 เพราะผิดแล้วกระทบหนัก ผมจะเขียนกำกับไว้เลยว่า "this is the orientation copy" เพื่อให้คนแก้รู้ว่ามันซ้ำโดยตั้งใจ

DAG: One-way reference

Circular reference มีสองแบบ

แบบแรก Link cycle A บอกให้อ่าน B แล้ว B บอกให้กลับไปอ่าน A agent ก็จะโหลดไฟล์ที่มีอยู่ใน context แล้วซ้ำอีกรอบ

แบบที่สอง Definition cycle นิยาม "Session" ด้วย "Token" แล้วนิยาม "Token" ด้วย "Session" ผู้อ่านไม่ได้อะไรใหม่จากทั้งสองนิยาม วิธีแก้คือเรียง glossary จากคำพื้นฐานที่สุดไปหาคำที่ซับซ้อน และคำแต่ละคำนิยามได้ด้วยคำที่อยู่ก่อนหน้าเท่านั้น

ส่วนที่เหลือของหัวข้อนี้จะพูดถึงแบบแรก

Rule: Links only point down

L0 (AGENTS.md)  →  L1 (docs/System)  →  L2 (instructions, skills)
  • ห้าม L1 ชี้กลับขึ้นไป L0
  • ห้ามไฟล์ในชั้นเดียวกันชี้หากันเอง
  • ถ้าทุก link ชี้ลงอย่างเดียว ก็ไม่มีทางเกิด cycle

โครงสร้างแบบนี้มีชื่อเรียกว่า DAG (Directed Acyclic Graph) คือ link ทุกเส้นมีทิศทาง (Directed) และถ้าเดินตาม link ไปเรื่อยๆ จะไม่มีทางวนกลับมาที่ไฟล์เดิม (Acyclic) ซึ่งก็คือรูปแบบเดียวกับ dependency ระหว่าง project หรือ layered architecture ที่ dev คุ้นเคยอยู่แล้ว

ในชั้น L2 ทีมผมใช้กฎที่แรงกว่านั้นอีก คือ instruction file ต้อง standalone ห้ามชี้ไปที่ไฟล์อื่นเลย ถ้า rule ต้องใช้ข้อมูลอย่าง path หรือค่าคงที่ ให้เขียนลงไปใน rule เลย ข้อยกเว้นเดียวคือชี้ไปที่ skill หรือ template เพราะนั่นคือ action ไม่ใช่การอ่านเพิ่ม เหตุผลคือ instruction file ถูกโหลดเพราะ applyTo match ซึ่งตอนนั้น AGENTS.md อยู่ใน context แล้ว ชี้กลับไปก็เปลือง context เปล่าๆ

ของจริงใน repo

ถึงจะมีกฎนี้ แต่พอลองเช็คดู ก็ยังเจอ 4 cycle

docs/System/ARCHITECTURE.md -> CONSTITUTION.md -> docs/System/ARCHITECTURE.md
docs/System/ARCHITECTURE.md -> docs/System/ONBOARDING.md -> docs/System/ARCHITECTURE.md
AGENTS.md -> docs/System/ARCHITECTURE.md -> docs/System/ONBOARDING.md -> AGENTS.md
AGENTS.md -> docs/System/ARCHITECTURE.md -> AGENTS.md

จะเห็นได้ว่าทุก cycle อยู่ในชั้น L0/L1 ส่วนชั้น L2 ไม่มีเลย เพราะมีกฎ standalone บังคับอยู่ ปัญหาเลยอยู่ที่ไฟล์ชั้นบนที่ไม่มีใครเขียนกฎไว้ให้

พอไล่ดูทีละ link จะแบ่งสาเหตุได้สามแบบ

1. หัวไฟล์ที่ทำหน้าที่เป็นแผนที่ซ้ำ ทั้ง ARCHITECTURE.md และ ONBOARDING.md เปิดไฟล์ด้วยประโยคทำนองนี้

How to set up a machine is in [ONBOARDING.md](ONBOARDING.md); how an agent
operates here is in [AGENTS.md](../../AGENTS.md); the binding principles are
in [CONSTITUTION.md](../../CONSTITUTION.md).

ซึ่งเนื้อหานี้ซ้ำกับแผนที่ใน AGENTS.md ที่บอกอยู่แล้วว่า ARCHITECTURE.md คือระบบคืออะไร และ ONBOARDING.md คือการตั้งเครื่อง agent ทุกตัวเข้ามาทาง L0 อยู่แล้ว แผนที่จึงอยู่ใน context ก่อนจะเปิด L1 เสมอ

วิธีแก้ ลบแผนที่ซ้ำออกจาก L1 ให้เหลือแค่ประโยคที่บอกว่าไฟล์นี้คืออะไร

What this repository is: the pattern, the structure, the components, and the
explicit technology choices and limits.

2. อ้างหลักการแทนที่จะเขียน fact เช่นช่อง Notes ของแถว playground/ ในตาราง Project Structure ของ ARCHITECTURE.md

Writable; never a reference.
See the constitution's Experimental Quarantine.

ประโยคหลังไม่ได้เพิ่มข้อมูลอะไร เพราะ fact อยู่ในประโยคแรกครบแล้ว และ constitution ก็ถูกโหลดจาก L0 อยู่แล้ว

วิธีแก้ เขียน fact ลงไปตรงๆ แล้วตัดการอ้างอิงทิ้ง ซึ่งก็คือกฎ standalone เดียวกับที่ใช้กับ instruction file นั่นเอง

Writable; never used as a reference for new code.

3. ไฟล์ระดับเดียวกันต้องใช้ข้อมูลของกันและกัน อันนี้เป็นกรณีที่ต้องคิดมากที่สุด ONBOARDING.md ต้องบอกคนตั้งเครื่องใหม่ว่า project ไหน build ไม่ผ่านบ้าง ซึ่งรายการนี้อยู่ในหัวข้อ Known Gaps ของ ARCHITECTURE.md เลยต้องชี้ข้ามไป ส่วน ARCHITECTURE.md ก็ชี้กลับมาที่ ONBOARDING.md เรื่องการ build

ทางแก้มีสองทาง ขึ้นอยู่กับว่าเนื้อหาที่ใช้ร่วมกันเป็นแบบไหน

กฎสั้นๆ ที่ทุกคนต้องรู้ ยกขึ้นไปที่ไฟล์แม่ (L0) เพราะถูกโหลดทุก request อยู่แล้ว AGENTS.md shared rule a.md b.md รายละเอียดยาว หรือเปลี่ยนบ่อย แยกลงเป็นไฟล์ลูก (leaf) เพราะถ้ายกขึ้น L0 แผนที่จะบวม ARCHITECTURE.md ONBOARDING.md KNOWN-GAPS.md shared detail
ไฟล์ระดับเดียวกันไม่ชี้หากันเอง ข้อมูลที่ใช้ร่วมกันต้องย้ายไปอยู่ชั้นบนหรือชั้นล่าง

Known Gaps เป็นรายการที่เปลี่ยนบ่อยและยาวขึ้นเรื่อยๆ ผมเลยเลือกแยกเป็นไฟล์ใหม่ docs/System/KNOWN-GAPS.md แล้วให้ทั้ง ARCHITECTURE.md และ ONBOARDING.md ชี้ลงไปหาไฟล์นี้แทนที่จะชี้หากันเอง

Note: ไฟล์นี้อยู่ในโฟลเดอร์ docs/System/ เหมือนเดิมก็จริง แต่ layer กำหนดจากหน้าที่ของไฟล์ ไม่ได้กำหนดจากโฟลเดอร์ KNOWN-GAPS.md เป็นใบไม้ที่ไม่ชี้ไปไหนต่อ จึงถือเป็น L2

ส่วนประโยคใน ONBOARDING.md ที่อธิบายว่า AGENTS.md เป็นไฟล์หลักและ CLAUDE.md เป็นแค่ adapter อันนี้เป็นเรื่องที่ L0 เป็นเจ้าของอยู่แล้ว ก็ลบทิ้งได้เลย

ผลลัพธ์หลังแก้

ก่อน: เจอ 4 cycle หลัง: อ้างอิงทางเดียว (DAG) AGENTS.mdCONSTITUTION.mdARCHITECTURE.mdONBOARDING.md AGENTS.mdCONSTITUTION.mdARCHITECTURE.mdONBOARDING.mdKNOWN-GAPS.md ชี้ขึ้น หรือชี้ข้าง ชี้ลง
เส้นสีแดงคือ link ที่ชี้ขึ้นหรือชี้ข้าง หลังแก้ ข้อมูลที่ใช้ร่วมกันย้ายลงไปอยู่ใน KNOWN-GAPS.md

แล้วคนที่เปิดไฟล์ L1 ตรงๆ ล่ะ?

ข้อนี้เป็น trade-off ที่ต้องยอมรับ คนที่เปิด ARCHITECTURE.md จาก Azure DevOps หรือ GitHub โดยตรงจะไม่มี link พากลับขึ้นไป ผมเลือกให้ README.md เป็นทางเข้าของคน และ AGENTS.md เป็นทางเข้าของ agent ส่วนในไฟล์ L1 ถ้าอยากได้ breadcrumb จริงๆ ก็ทำได้ แต่ให้เป็นการบอกตำแหน่ง ไม่ใช่การสั่งให้ไปอ่าน

สรุป

  • L0 เป็นแผนที่ AGENTS.md บอกแค่ว่าแต่ละเรื่องอยู่ไฟล์ไหนและควรอ่านเมื่อไร รายละเอียดอยู่ในไฟล์ชั้นล่าง
  • SSOT + DRY หนึ่งเรื่องมีไฟล์เจ้าของไฟล์เดียว ไฟล์อื่นใช้ pointer บรรทัดเดียวชี้ไปหา
  • DAG link ชี้ลงอย่างเดียว ถ้าไฟล์ชั้นเดียวกันต้องใช้ข้อมูลร่วมกัน ให้ยกขึ้นไฟล์แม่หรือแยกลงเป็นไฟล์ลูก
  • Vendor file เป็นแค่ adapter แก้ที่ .agents/ ที่เดียว แล้วใช้ script sync ออกไปให้แต่ละเจ้า
  • กฎที่เครื่องตรวจได้ ให้เครื่องตรวจ เช่น link ที่เสีย link ที่ชี้ขึ้น หรือ copy ที่ไม่ตรงกับ source ใส่ไว้ใน CI เพราะกฎที่เขียนเป็นข้อความอย่างเดียว สุดท้ายก็มีวันหลุด

Ref

Ads

ขอบคุณที่อ่านกันมาถึงตรงนี้

เพิ่มเติม เผื่อใครอยากติดตามผลงานสามารถ Follow Github ส่วนตัวของผม หรือจะกดปุ่มสีเหลืองๆ ด้านล่างเพื่อเข้าไปให้กำลังใจเป็นค่า เหล้า กาแฟ สำหรับสร้างผลงานชิ้นต่อไป

ถ้าบทความนี้มีประโยชน์ สนับสนุนบล็อกได้ด้วยกาแฟสักแก้ว

เลี้ยงกาแฟสักแก้ว