🔁 ทวนก่อนเริ่ม — คำพวกนี้จะโผล่ในบทนี้
- Claude Code (บท 86) — AI ที่อ่านและแก้ไฟล์ในโปรเจกต์ได้เอง ·
ในบทนั้นเราเปิดโปรเจกต์ Next.js แล้วเจอไฟล์
CLAUDE.mdที่มีบรรทัดเดียวคือ@AGENTS.md— บทนี้คือเรื่องเต็มของไฟล์นั้น - Explore → Plan → Code → Evaluate → Commit (บท 87) — ขั้น Evaluate คือ "ตรวจกับ requirement เดิมทีละข้อ"
- prompt ที่ดี (บท 88–89) — บอกให้ชัด · บอกสิ่งที่ห้ามแตะ · แบ่งหัวข้อด้วย Markdown (
#หัวข้อใหญ่ ·##หัวข้อรอง ·-รายการ) - checklist 8 ข้อ (บท 118) และ prompt ยาว 12 ข้อของ Lab 2 (บท 119) — ของสองอย่างนี้จะกลับมาในบทนี้
🧰 เวอร์ชันที่ใช้ตรวจ: Claude Code 2.1.318 · ไฟล์ทุกไฟล์ในบทก็อปจาก repo ของคอร์ส branch day3-start ตรง ๆ
(เครื่องตรวจใช้ Ruby 4.0.9 · Rails 8.1.4 ส่วนไฟล์ใน repo เขียน 4.0.5 · 8.1.3 — บทนี้ไม่ได้รัน Rails เลย จึงไม่มีผล)
📍 บทนี้เริ่มจาก: โปรเจกต์ wongnok-blog-all-in-one ที่เปิดไว้ในบท 120 (branch my-day3 ที่แตกจาก day3-start + seeds 30 บทความที่ commit แล้ว) ·
ตามไม่ทันให้ทำบท 120 ก่อน · บทนี้อ่านไฟล์เป็นหลัก · ที่ลองแก้ในบท จะบอกวิธีคืนค่าทุกครั้ง
ปัญหา: AI คือพนักงานใหม่ที่จำบทสนทนาเมื่อวานไม่ได้
ใน Lab 2 (บท 119) เราเขียน prompt ยาว 12 ข้อ เพื่อให้ AI สร้างหมวดอาหารได้ถูกกฎของร้านในรอบเดียว · ปัญหาคือพรุ่งนี้เปิดแชทใหม่ AI ไม่เห็นข้อความแชทของเมื่อวาน ต้องแปะใหม่ทั้งก้อน
ทางแก้ของคอร์สนี้คือเขียน prompt ลงไฟล์ แล้วเก็บไว้ในโฟลเดอร์ prompts/ ใน repo ·
อยากใช้เมื่อไหร่ก็เรียกไฟล์นั้น · เจอ AI ทำพลาดก็แก้ไฟล์ให้ดีขึ้น · และไฟล์เข้า git ไปพร้อมโค้ด เพื่อนในทีมได้ใช้ด้วย
เทียบกับ React: ข้อความในแชทเหมือน useState (บท 73) ที่หายเมื่อรีเฟรชหน้า ·
ไฟล์ใน prompts/ เหมือนของที่เขียนลงดิสก์ — ปิดเปิดใหม่กี่รอบก็ยังอยู่
นึกถึงร้านอาหารที่ได้พนักงานพาร์ตไทม์คนใหม่ทุกกะ · เก่ง ทำงานไว แต่ไม่รู้ว่ากะก่อนคุยอะไรกันไว้ ·
ถ้าหัวหน้าต้องยืนอธิบายกฎร้านปากเปล่าทุกกะ วันไหนลืมพูดข้อไหน ข้อนั้นก็พลาด
ร้านที่ฉลาดจะทำแฟ้มคู่มือวางไว้ให้อ่านก่อนเริ่มงาน: หน้าแรกเป็นกฎถาวรของร้าน (00-context.md) ·
หน้าถัด ๆ ไปเป็นคู่มือของแต่ละงาน (01-views.md · 02-styling.md …) · เจอใครทำพลาดก็เติมหน้าคู่มือ
ที่ไม่เหมือนกัน: พนักงานจริงที่อ่านคู่มือแล้วไม่เข้าใจจะถามกลับ · AI ส่วนใหญ่ไม่ถาม มันเดาแล้วทำต่อด้วยความมั่นใจ · และพนักงานจริงที่ฝ่าฝืนกฎร้านโดนเตือนได้ แต่ AI ที่อ่านคู่มือแล้วก็ยังทำผิดได้ — เรื่องนี้สำคัญมาก เดี๋ยวมีหัวข้อของมันเอง
สไลด์หน้า 4 บอกว่าโฟลเดอร์นี้จะโตไปทีละไฟล์ตลอดคอร์ส · ตอนนี้ (หลังบท 120) ใน repo มีแค่ไฟล์แรก:
| ไฟล์ | เรื่อง | เกิดตอนไหน |
|---|---|---|
00-context.md | stack + กฎถาวรของโปรเจกต์ | มีแล้ว — อ่านกันในบทนี้ |
01-views.md | บล็อก A · View | บท 126 (Lab Vibe 1) |
02-styling.md | บล็อก B · CSS | บท 129 (Lab Vibe 2) |
03-hotwire.md | บล็อก C · Hotwire | บท 135 (Lab Vibe 3) |
04-content.md · 05-jobs.md · 06-caching.md | rich text · งานเบื้องหลัง · cache | Day 3 Part 2 (ยังไม่มาในชุดนี้) |
07-auth.md · 08-testing.md | login · การเขียนเทส | Day 4 |
FINAL.md | เอาทุกไฟล์มาต่อกัน | ท้ายคอร์ส |
สไลด์เล่าว่าท้ายคอร์สจะเอาทุกไฟล์มาต่อเป็น FINAL.md แล้วสั่ง AI สร้างแอปใหม่ทั้งแอป ·
ไฟล์นั้นจึงเป็นความรู้ทั้งคอร์สที่เขียนออกมาเป็นตัวหนังสือ — ไฟล์จะดีได้เท่าที่เราเข้าใจเรื่องที่เรียนเท่านั้น
- session · context
- session = บทสนทนาหนึ่งรอบกับ Claude Code (ปิดแล้วเปิดใหม่ = session ใหม่) · context = ทุกอย่างที่ AI "เห็น" ใน session นั้น (ข้อความแชท · ไฟล์ที่อ่าน · กฎที่โหลดมา) · ถาม senior ได้ว่า "กฎนี้ควรอยู่ในไฟล์ที่โหลดทุก session หรือพิมพ์บอกเฉพาะงานนี้"
- prompt file
- prompt ที่เขียนเก็บเป็นไฟล์
.mdใน repo แทนการพิมพ์ใหม่ทุกครั้ง · แก้ได้ · เข้า git ได้ · ทั้งทีมใช้ร่วมกันได้ · ถามทีมได้ว่า "เรามีโฟลเดอร์ prompt ที่ใช้ร่วมกันไหม หรือทุกคนพิมพ์เอง"
ทุกไฟล์รายบล็อกมี 4 ส่วนเหมือนกัน
ไฟล์ 01–08 ใช้แม่แบบเดียวกันหมด (สไลด์หน้า 4) · เป็น Markdown ธรรมดา มี 4 หัวข้อ ·
ข้างล่างคือแม่แบบ ยังไม่ใช่ไฟล์จริงใน repo (ไฟล์จริงไฟล์แรกเกิดในบท 126):
## เป้าหมาย
ฟีเจอร์ที่อยากได้ เขียนเป็นภาษาคน
## ข้อกำหนด
- มาจากคำศัพท์ที่เพิ่งเรียนในบล็อกนี้
- ...
## ห้าม
- มาจากกับดักที่เพิ่งเจอในบล็อกนี้
## เกณฑ์ตรวจรับ
- [ ] คือ checklist ของบล็อกนี้
- [ ] ...
## เป้าหมาย= "อยากได้อะไร" เล่าแบบที่ลูกค้าเล่า ไม่ต้องมีศัพท์ · เช่น "หน้ารายการบทความที่อ่านรู้เรื่อง"## ข้อกำหนด= "ต้องทำด้วยวิธีไหน" · ตรงนี้ใช้ศัพท์ที่เพิ่งเรียน เช่น "ใช้ partial" "ใช้ includes" — ถ้าเราไม่รู้ศัพท์ เราก็สั่งไม่ได้## ห้าม= กับดักที่เราเพิ่งตกมาเองในบล็อกนั้น · เขียนไว้ให้ AI ไม่ตกซ้ำ## เกณฑ์ตรวจรับ= รายการที่ตรวจได้จริงทีละข้อ ·- [ ]คือช่องสี่เหลี่ยมว่างไว้ติ๊ก (ติ๊กแล้วเขียน- [x]) · ใช้ตรวจงานที่ AI ส่งมา
ทั้ง 4 ส่วนไม่ใช่ของใหม่ · เป้าหมายกับข้อกำหนดคือ "บอกให้ชัด" ของบท 88 · ห้ามคือ "บอกสิ่งที่ห้ามแตะ" ของบท 89 · เกณฑ์ตรวจรับคือขั้น Evaluate ของบท 87 · ต่างกันแค่ว่าครั้งนี้จัดลงช่องเดิมทุกครั้ง
สไลด์พูดไว้ดีมาก: สามส่วนล่าง (ข้อกำหนด · ห้าม · เกณฑ์ตรวจรับ) ไม่ใช่งานเพิ่ม — มันคือเนื้อหาที่เพิ่งเรียนในบล็อกนั้น แค่เขียนลงไฟล์ · ถ้าเขียนส่วนไหนไม่ออก แปลว่าเรื่องในบล็อกนั้นยังไม่เข้าใจ ให้ย้อนกลับไปอ่าน ไม่ใช่ไปถาม AI ให้เขียนแทน
ใบสั่งงานที่พิมพ์ช่องไว้แล้ว "เมนู · วิธีทำ · ห้ามใส่ (ลูกค้าแพ้) · ชิมผ่านเมื่อ…" · คนเขียนเห็นทันทีว่าลืมกรอกช่องไหน · และช่อง "ชิมผ่านเมื่อ…" คือสิ่งที่หัวหน้าใช้ตรวจจานก่อนเสิร์ฟ
ที่ไม่เหมือนกัน: ใบสั่งงานจริงเขียนครั้งเดียวทิ้ง · ไฟล์ prompt ของเราถูกแก้ซ้ำเรื่อย ๆ ทุกครั้งที่ AI พลาดแบบใหม่ · และคนที่ต้องติ๊กเกณฑ์ตรวจรับทีละข้อคือเรา ไม่ใช่ AI
- goal · requirements · constraints
- ชื่อภาษาอังกฤษของ เป้าหมาย · ข้อกำหนด · ห้าม · constraint = "ข้อจำกัด/สิ่งที่ห้ามทำ" · พิมพ์บอก AI ได้ว่า "Goal: … Requirements: … Constraints: do NOT add a migration"
- acceptance criteria
- "งานนี้ถือว่าเสร็จเมื่อ…" เขียนเป็นข้อที่ตรวจได้จริง (เคยเจอในบท 119) · ถาม senior ได้ว่า "acceptance criteria ของ ticket นี้คืออะไร ผมจะได้รู้ว่าเสร็จเมื่อไหร่"
เปิด prompts/00-context.md ของจริง
ไฟล์ 00 ต่างจากไฟล์อื่น: มันไม่ใช่งานของบล็อกไหน แต่เป็น "หน้าแรกของคู่มือ" ที่ใช้ทุกงาน จึงไม่มี 4 ส่วนข้างบน ·
เปิดในโฟลเดอร์โปรเจกต์ด้วย cat prompts/00-context.md (หรือเปิดใน VS Code) · ผมแบ่งดูเป็นสองครึ่ง
ครึ่งบน — บอก AI ว่าร้านเราใช้อุปกรณ์อะไร
# บริบทของโปรเจค wongnok_blog
## Stack
- Ruby 4.0.5 / Rails 8.1.3 / PostgreSQL 18
- Propshaft + importmap (ห้ามใช้ Sprockets, ห้ามแนะนำ npm หรือ node_modules)
- tailwindcss-rails (Tailwind v4) + daisyUI 5 ผ่าน @plugin ในไฟล์ CSS
ห้ามสร้าง tailwind.config.js — Tailwind v4 ไม่อ่านไฟล์นั้น
- Solid Queue / Solid Cache / Solid Cable
- Active Storage -> MinIO (S3-compatible)
- Minitest (ไม่ใช่ RSpec)
## รูปแบบโค้ด
- ข้อความที่ผู้ใช้เห็นเป็นภาษาไทย ผ่าน i18n
- ใช้ params.expect ไม่ใช่ params.require(...).permit(...)
- ตั้งชื่อตาม convention ของ Rails เสมอ
- ห้ามมี `<script>` ใน view
## Stack= รายชื่อเครื่องมือหลักของโปรเจกต์ · ไม่ต้องรู้จักทุกชื่อตอนนี้ ส่วนใหญ่จะได้เรียนวันนี้: Propshaft + importmap (บท 127 — ระบบส่งไฟล์ CSS/JS ไปถึงเบราว์เซอร์) · Tailwind v4 + daisyUI (บท 128) · Solid Cable (บท 133)- ที่เหลือเป็นของ Part 2 กับ Day 4 · ตอนนี้แค่รู้ว่ามีอยู่: Solid Queue (งานเบื้องหลัง) · Solid Cache (cache) · Active Storage (ระบบแนบไฟล์/รูป) ·
->MinIO = เก็บไฟล์ไว้ที่ "โกดังเก็บไฟล์" ที่ docker เปิดให้ในบท 120 (S3-compatible = ใช้วิธีคุยแบบเดียวกับบริการเก็บไฟล์ S3 ของ Amazon) · Minitest กับ RSpec = เครื่องมือเขียน test สองยี่ห้อ โปรเจกต์นี้ใช้ตัวแรก @pluginในบรรทัด daisyUI เป็นคำสั่งในไฟล์ CSS ของ Tailwind (บท 128) · ไม่ใช่@แบบดึงไฟล์ที่จะเจอในหัวข้อ CLAUDE.md ข้างล่าง- ทำไมต้องบอก stack? เพราะ AI ไม่รู้ว่าโปรเจกต์เราใช้อะไร มันจะเดาจากสิ่งที่พบบ่อยในอินเทอร์เน็ต · เช่น Rails รุ่นเก่าใช้ Sprockets (ระบบส่งไฟล์ CSS/JS รุ่นก่อน Propshaft) · Tailwind รุ่นเก่าใช้
tailwind.config.js - สังเกตคำว่า "ห้าม" ในวงเล็บและบรรทัด tailwind.config.js — เป็นของรุ่นเก่าที่ AI มีโอกาสเดามาใส่ · สไลด์หน้า 5 บอกว่าไฟล์แบบนี้ "โตขึ้นทุกครั้งที่เจอ AI ทำพลาด" — ข้อห้ามพวกนี้คือหน้าตาของกฎที่เกิดแบบนั้น
## รูปแบบโค้ด= กติกาการเขียน ·params.expectคือท่าของ Rails 8 ที่เรียนในบท 117 · ห้าม<script>ใน view จะเข้าใจเหตุผลตอนเจอ Stimulus ในบท 134- i18n = ระบบแปลข้อความของ Rails (เขียนข้อความแยกไว้ในไฟล์ภาษา) · บทเรียนของเราไม่ใช้ i18n เขียนภาษาไทยตรง ๆ ใน view · บท 126 จะพาแก้บรรทัดนี้ให้ตรงกับโค้ดจริง — เป็นตัวอย่างแรกของ "เจอไม่ตรงแล้วแก้ไฟล์"
ครึ่งล่าง — checklist 8 ข้อของ Day 2 ย้ายมาอยู่ตรงนี้
## กฎที่ห้ามลืม (จากวันก่อน)
1. validates uniqueness ต้องมี unique index ที่ DB คู่เสมอ
2. validates presence ต้องมี NOT NULL ที่ DB คู่เสมอ
3. คอลัมน์ที่ใช้ค้นหาหรือเป็น foreign key ต้องมี index
4. ระบุ dependent: ของทุก association เสมอ และเลือกจากกฎธุรกิจ
5. ตรงที่วน each ต้องมี includes
6. index action ต้องมี limit หรือ pagination
7. migration ต้องไม่ล็อกตารางที่มีข้อมูลเยอะ — ใช้ expand-contract
8. strong parameters ไม่ใช่ authorization — แยกให้ออก
## รูปแบบการตอบ
- บอกชื่อไฟล์ทุกไฟล์ที่แก้หรือสร้าง
- ถ้าต้องมี migration ให้เขียนมาด้วย
- อย่าเพิ่ง refactor อย่างอื่นที่ไม่ได้ขอ
8 ข้อนี้คือchecklist ตรวจงาน AI ของบท 118 ทั้งชุด แค่เปลี่ยนจาก "คำถามที่เราถามตอนตรวจ" มาเป็น "กฎที่บอก AI ก่อนเริ่มงาน" · ถ้าข้อไหนอ่านแล้วไม่แน่ใจ ย้อนไปบทนั้น:
| ข้อ | แปลเป็นภาษาคน | เรียนในบท |
|---|---|---|
| 1–2 | กฎใน model (validation) ต้องมีกฎที่ตัวตู้ (constraint ของ DB) คู่กันเสมอ เพราะกันคนละรู | 110 · 111 |
| 3 | ช่องที่ใช้ค้นหา และช่อง …_id ต้องมีป้ายแท็บ (index) ไม่งั้นไล่หาทีละแผ่น |
109 |
| 4 | ลบแม่แล้วลูกไปไหน ต้องเลือกเองจากกฎของธุรกิจ | 116 |
| 5 | วนลูปแล้วแตะของที่ผูกกัน ต้องหยิบทีเดียวทั้งตะกร้า (N+1) | 114 |
| 6 | หน้ารายการห้ามดึงทุกแถว ต้องจำกัดจำนวนหรือแบ่งหน้า | 113 |
| 7 | migration ห้ามปิดลิ้นชักนานจนแอปค้าง | 109 |
| 8 | strong params คัดช่องที่รับ · ไม่ได้ตรวจว่าใครมีสิทธิ์ (เรื่องคนคือ Day 4) | 117 |
## รูปแบบการตอบ = บอกว่าอยากได้คำตอบหน้าตาแบบไหน — คือหลักข้อ 5 "บอกรูปแบบผลลัพธ์" ของบท 89 ·
ข้อสุดท้าย "อย่าเพิ่ง refactor (ปรับโค้ดส่วนอื่นให้สวยขึ้น) อย่างอื่นที่ไม่ได้ขอ" กัน AI แก้ไฟล์เกินงานจนเราตรวจไม่ไหว
ไฟล์นี้ยาว 35 บรรทัด (ผมนับด้วย wc -l prompts/00-context.md) · ตลอด Day 3 จะมีกฎเพิ่มอีก 4 ข้อในบท 135 ·
สไลด์หน้า 6 พูดถึง "กฎทั้ง 12 ข้อ" ไว้ล่วงหน้า — ตอนนี้ในไฟล์ยังมี 8
CLAUDE.md — ป้ายหน้าห้องพนักงานที่ Claude Code อ่านเองทุกครั้ง
แฟ้มคู่มือจะมีประโยชน์ก็ต่อเมื่อพนักงานได้อ่าน · ถ้าเราต้องพิมพ์ว่า "อ่าน 00-context.md ก่อนนะ" ทุกครั้ง วันไหนลืมก็เหมือนไม่มีไฟล์
Claude Code แก้ปัญหานี้ด้วยไฟล์ชื่อ CLAUDE.md · เอกสารบอกว่ามันอ่านไฟล์นี้เองตอนเริ่มทุก session โดยเราไม่ต้องสั่ง
เปิดดูของจริงที่ root ของโปรเจกต์ (ระดับเดียวกับ Gemfile):
$ cat CLAUDE.md
# wongnok_blog
@prompts/00-context.md
## คำสั่งที่ใช้บ่อย
- bin/dev รัน web + tailwind watch
- bin/rails test รัน test ทั้งหมด
- bin/rails db:prepare เตรียมฐานข้อมูล
- bin/rails routes ดู route ทั้งหมด
- docker compose up -d postgres + minio
$ cat CLAUDE.md= คำสั่งที่ผมพิมพ์ ($คือเครื่องหมายบอกว่า "บรรทัดนี้พิมพ์ใน terminal" ไม่ต้องพิมพ์ตาม) ·cat= พิมพ์เนื้อไฟล์ออกมาให้ดู@prompts/00-context.md= หัวใจของไฟล์ ·@ตามด้วยที่อยู่ไฟล์ = "ดึงเนื้อไฟล์นั้นมาวางตรงนี้ด้วย" (เอกสารเรียกว่า import) · ผลคือ 00-context.md ถูกโหลดทุก session ตาม CLAUDE.md ไปด้วย- ที่อยู่ไฟล์นับจากตำแหน่งของไฟล์ที่เขียน @ (ไม่ใช่จากโฟลเดอร์ที่เราเปิด) · CLAUDE.md อยู่ที่ root จึงชี้ไป
prompts/00-context.mdได้ตรง ๆ ## คำสั่งที่ใช้บ่อย= โน้ตบอก AI ว่าโปรเจกต์นี้รันอะไรยังไง · AI จะได้ไม่เดาคำสั่งเอง (bin/devละเอียดในบท 128)
เทียบกับ TypeScript: @ คล้าย import (บท 71) ที่ดึงของจากไฟล์อื่นมาใช้ ·
ต่างกันตรงที่ @ เอาเนื้อทั้งไฟล์มาวางให้ AI อ่านเลย ไม่ได้เลือกหยิบเฉพาะชิ้น
CLAUDE.md คือป้ายหน้าห้องพนักงานที่ทุกคนต้องอ่านก่อนเข้างาน · บนป้ายเขียนสั้น ๆ ว่า "อ่านแฟ้มกฎถาวรที่แขวนข้างป้ายด้วย" (บรรทัด @) ·
พนักงานใหม่ทุกกะเลยได้อ่านกฎครบโดยหัวหน้าไม่ต้องพูดสักคำ
ที่ไม่เหมือนกัน: พนักงานคนจริงอ่านป้ายแล้วอาจข้ามแฟ้ม · Claude Codeโหลดทั้งป้ายและแฟ้มเข้ามาให้เองทุกครั้ง (ไม่ได้ขึ้นกับความขยัน) · แต่ "ได้อ่าน" ไม่ได้แปลว่า "ทำตามทุกข้อ" — ยังเป็นเรื่องเดียวกับที่ค้างไว้
- CLAUDE.md · project instructions
- ไฟล์คำสั่งประจำโปรเจกต์ที่ Claude Code โหลดเองตอนเริ่ม session · เอกสารเรียกว่า project instructions หรือ memory files · ถาม senior ได้ว่า "repo นี้มี CLAUDE.md ไหม มีกฎอะไรที่ทีมตกลงกันไว้"
- import (
@path) @ที่อยู่ไฟล์ใน CLAUDE.md = ดึงไฟล์นั้นมาโหลดด้วย · ถ้าแค่อยากเขียนชื่อที่ขึ้นต้นด้วย@เฉย ๆ ไม่ให้ import เอกสารบอกให้ครอบด้วย backtick (`…`) · พิมพ์บอก AI ได้ว่า "our CLAUDE.md imports @prompts/00-context.md — follow those rules"
CLAUDE.md กับ AGENTS.md — ไฟล์เดียวกัน สองชื่อ
ใน repo ของคอร์ส CLAUDE.md ไม่ใช่ไฟล์ธรรมดา · ลองสั่ง ls -l (ดูรายละเอียดไฟล์) กับสองไฟล์นี้:
$ ls -l CLAUDE.md AGENTS.md
-rw-r--r--@ 1 tuwanon wheel 389 Nov 7 09:14 AGENTS.md
lrwxr-xr-x@ 1 tuwanon wheel 9 Nov 7 11:18 CLAUDE.md -> AGENTS.md
- ตัวอักษรแรกของบรรทัด:
-= ไฟล์ธรรมดา ·l= link (ป้ายชี้ไปไฟล์อื่น) CLAUDE.md -> AGENTS.md= "CLAUDE.md ชี้ไปที่ AGENTS.md" · เปิด CLAUDE.md ก็คือเปิด AGENTS.md- ขนาด: AGENTS.md
389ไบต์ (เนื้อจริง) · CLAUDE.md แค่9ไบต์ = ความยาวของคำว่าAGENTS.mdพอดี — ตัว link เก็บแค่ "ชื่อปลายทาง" ไม่ได้เก็บเนื้อ - ชื่อเจ้าของ (
tuwanon) · วันเวลา ·@หลังตัวอักษรชุดแรก (macOS ใส่เอง ไม่เกี่ยวกับ@ใน CLAUDE.md) ของคุณจะต่างจากนี้ · ที่ต้องตรงคือlและ-> AGENTS.md
ไฟล์ link แบบนี้เรียกว่า symlink (symbolic link) · ทำไมไม่ตั้งชื่อ CLAUDE.md ไปเลย?
เพราะ AGENTS.md เป็นชื่อที่ไม่ผูกกับยี่ห้อ · เอกสารของ Claude Code อธิบาย AGENTS.md ว่าเป็นไฟล์คำสั่งที่เขียนให้ AI coding agent ทั่วไป ·
ท่า symlink นี้เป็นหนึ่งในสองท่าที่เอกสารให้ไว้สำหรับ repo ที่อยากให้เครื่องมือหลายตัวใช้ไฟล์เดียวกัน
(อีกท่าคือ CLAUDE.md ที่มีบรรทัด @AGENTS.md แบบที่เจอในบท 86)
ห้องพนักงานห้องเดียว แต่ประตูติดป้ายสองแผ่น: "ห้องพนักงาน" กับ "Staff Room" · พนักงานคนไทยมองหาป้ายแรก คนต่างชาติมองหาป้ายที่สอง · ใครเดินเข้าป้ายไหนก็เจอห้องเดียวกัน ของข้างในชุดเดียวกัน · AGENTS.md คือตัวห้อง · CLAUDE.md คือป้ายแผ่นที่สอง
ที่ไม่เหมือนกัน: เขียนอะไรลงบนป้ายจริง ตัวหนังสือก็อยู่บนป้าย · แต่เขียนลง symlink ของจะไปลงในไฟล์ปลายทาง (ลองในกล่องทายผลข้างล่าง) · และถ้าลบตัวห้อง (AGENTS.md) ป้ายจะชี้ไปที่ว่าง เปิดไม่ได้ · (บน Windows บางเครื่อง ป้ายกลายเป็นกระดาษที่เขียนชื่อห้องไว้เฉย ๆ — ดูตารางอาการในภารกิจ)
เพิ่มบรรทัดใหม่ต่อท้าย CLAUDE.md ด้วยคำสั่งนี้ (echo '…' >> ไฟล์ = เขียนข้อความต่อท้ายไฟล์) แล้วถาม git ว่ามีไฟล์ไหนเปลี่ยน
(git status --short = git status แบบย่อ บรรทัดละไฟล์):
$ echo '- bin/rails console เปิดครัวทดลอง' >> CLAUDE.md
$ git status --short
git จะบอกว่าไฟล์ไหนถูกแก้ — CLAUDE.md · AGENTS.md · หรือทั้งสองไฟล์?
$ git status --short
M AGENTS.md
git บอกว่าไฟล์ที่ถูกแก้คือ AGENTS.md (M = modified) · CLAUDE.md ไม่โผล่เลย ·
เพราะเราเขียนผ่านป้าย แต่ของไปลงในห้อง · ตัวป้ายเองยังชี้ไปที่เดิม ไม่ได้เปลี่ยนอะไร
เอกสารของ Claude Code บอกด้วยว่า ถ้าสั่ง Claude แก้ CLAUDE.md ที่เป็น symlink มันจะไม่เขียนผ่านป้าย แต่จะถูกบอกให้ไปแก้ AGENTS.md ตรง ๆ แทน — ปลายทางคือไฟล์เดียวกับที่เราเพิ่งเห็น
คืนค่าก่อนไปต่อ: git restore AGENTS.md แล้ว git status --short อีกรอบ ต้องไม่มี AGENTS.md โผล่
คืนค่าด้วย git restore AGENTS.md ระบุชื่อไฟล์เสมอ · ถ้าคุณข้ามขั้น commit db/seeds.rb ในบท 120 แล้วใช้ git restore . (คืนทุกไฟล์) seeds จะกลับเป็นไฟล์เดิมที่มีแค่ comment (ข้อมูลตัวอย่าง 30 บทความหายไปจากไฟล์)
- symlink (symbolic link)
- ไฟล์ที่เก็บแค่ "ที่อยู่ของไฟล์อื่น" · เปิดแล้วระบบพาไปไฟล์ปลายทางให้เอง · สร้างด้วย
ln -s ปลายทาง ชื่อป้าย· ถาม senior ได้ว่า "CLAUDE.md ใน repo นี้เป็น symlink ไป AGENTS.md ใช่ไหม ถ้าจะแก้กฎต้องแก้ไฟล์ไหน" - AGENTS.md
- ไฟล์คำสั่งสำหรับ AI coding agent ที่ไม่ผูกกับเครื่องมือตัวใดตัวหนึ่ง · Claude Code อ่านได้ (เอกสารมีหัวข้อแยกให้) · พิมพ์บอก AI ได้ว่า "project rules live in AGENTS.md; CLAUDE.md is a symlink to it"
กฎ 3 ชั้น — ของทีมเข้า git ของส่วนตัวไม่เข้า
CLAUDE.md ไม่ได้มีแค่ไฟล์เดียว · ที่เราใช้บ่อยมีสามที่ และ Claude Codeอ่านทุกไฟล์ที่เจอแล้วเอามาต่อกัน ไม่มีไฟล์ไหนทับไฟล์ไหน (สไลด์หน้า 6 + เอกสาร):
| ไฟล์ | ใส่อะไร | ใครเห็น | เข้า git ไหม |
|---|---|---|---|
~/.claude/CLAUDE.md | นิสัยของเราเองที่ใช้กับทุกโปรเจกต์ เช่น "อธิบายเป็นภาษาไทย" | เราคนเดียว | ไม่ — อยู่ในโฟลเดอร์บ้านของเรา (~) ไม่ได้อยู่ใน repo ไหน |
./CLAUDE.md | กฎของโปรเจกต์นี้ที่ทั้งทีมต้องทำตาม | ทุกคนในทีม | เข้า — commit ไปพร้อมโค้ด |
./CLAUDE.local.md | ของส่วนตัวที่ใช้กับโปรเจกต์นี้เท่านั้น เช่น URL ของ DB ทดลองในเครื่องเรา | เราคนเดียว | ไม่ — ต้องใส่ชื่อไว้ใน .gitignore |
~= โฟลเดอร์บ้านของผู้ใช้ในเครื่อง (เช่น/Users/ชื่อคุณ) ·./= โฟลเดอร์ปัจจุบัน คือ root ของโปรเจกต์.gitignore= ไฟล์รายชื่อ "ของที่ git ต้องทำเป็นมองไม่เห็น" · repo ของคอร์สใส่CLAUDE.local.mdไว้ที่บรรทัด 40 แล้ว — ภารกิจท้ายบทจะพิสูจน์ด้วยคำสั่งจริง- ลำดับที่อ่าน: กว้างไปแคบ — ของเราเองทุกโปรเจกต์ → ของโปรเจกต์ → ของส่วนตัวในโปรเจกต์ · ถ้ากฎสองไฟล์ขัดกัน เอกสารบอกว่า Claude อาจเลือกข้อไหนก็ได้ — อย่าเขียนให้ขัดกันตั้งแต่แรก
พนักงานพาร์ตไทม์คนหนึ่งไปทำหลายร้าน · สมุดในกระเป๋า (~/.claude/CLAUDE.md) พกไปทุกร้าน จดนิสัยการทำงานของตัวเอง ·
ป้ายหน้าห้องพนักงาน (./CLAUDE.md) เป็นของร้าน ทุกคนเห็นเหมือนกัน ·
โพสต์อิทในล็อกเกอร์ของตัวเองที่ร้านนี้ (CLAUDE.local.md) ใช้เฉพาะร้านนี้ คนอื่นไม่เห็น
ที่ไม่เหมือนกัน: คนจริงรู้ว่าป้ายร้าน "ใหญ่กว่า" โน้ตส่วนตัว ถ้าขัดกันก็เชื่อป้าย ·
Claude Code ไม่มีลำดับใหญ่-เล็กแบบนั้น — อ่านต่อกันหมดแล้วต้องเลือกเอง · และล็อกเกอร์จริงล็อกได้เลย แต่ CLAUDE.local.md "ไม่เข้า git" ได้ก็เพราะ .gitignore เท่านั้น ลืมใส่ = หลุดขึ้น GitHub
เอาไปใช้ที่โปรเจกต์อื่นที่มีแค่ AGENTS.md (ไม่มี CLAUDE.md): เอกสารเตือนว่าสร้าง CLAUDE.local.md เพิ่มแล้ว Claude Code จะเลิกอ่าน AGENTS.md ·
repo ของคอร์สมี symlink CLAUDE.md อยู่แล้ว จึงไม่เจอปัญหานี้
- user · project · local scope
- ขอบเขตของไฟล์คำสั่ง: ทุกโปรเจกต์ของเรา · โปรเจกต์นี้ทั้งทีม · โปรเจกต์นี้แค่เรา · ถาม senior ได้ว่า "กฎข้อนี้ควรอยู่ project scope ให้ทั้งทีมใช้ หรือ local scope แค่ผม"
.gitignore- รายชื่อไฟล์ที่ git ไม่สนใจ (ไม่โผล่ใน
git status· ไม่ถูก commit) · ตรวจว่าไฟล์ไหนโดนกันด้วยgit check-ignore -v ชื่อไฟล์· พิมพ์บอก AI ได้ว่า "make sure CLAUDE.local.md is in .gitignore before I add secrets to it"
คำแนะนำ ไม่ใช่การบังคับ
นี่คือประโยคที่สไลด์หน้า 6 บอกว่า "ต้องพูดให้ตรง": CLAUDE.md คือคำแนะนำ · Claude พยายามทำตาม แต่ไม่รับประกัน · เอกสารของ Claude Code พูดแบบเดียวกัน — ไฟล์พวกนี้คือข้อมูลประกอบ ไม่ใช่การตั้งค่าที่บังคับได้
ดูหลักฐานใน repo เดียวกันเลย · กฎข้อ 6 ใน 00-context.md บอกว่า "index action ต้องมี limit หรือ pagination" แต่โค้ดตอนนี้คือ:
# app/controllers/posts_controller.rb (branch day3-start)
# GET /posts or /posts.json
def index
@posts = Post.all
end
Post.all ไม่มี limit · กฎในไฟล์ไม่ได้แก้โค้ดให้เอง และไม่ได้ห้ามใครเขียนโค้ดผิดกฎ (โค้ดนี้มาจาก scaffold — เราจะแก้ด้วยการแบ่งหน้าในบท 126) ·
AI ก็เหมือนกัน: อ่านกฎแล้ว ส่วนใหญ่ทำตาม แต่บางครั้งยังพลาด
แล้วกฎที่พลาดไม่ได้จริง ๆ ไปอยู่ไหน? สไลด์ตอบว่า test หรือ CI · repo นี้มีของพวกนี้อยู่แล้วบางส่วน:
- CI (continuous integration) = เครื่องของ GitHub ที่รันตรวจโค้ดให้อัตโนมัติ · ไฟล์
.github/workflows/ci.ymlสั่งให้มันทำงานทุกครั้งที่เปิด pull request (คำขอรวมโค้ดจาก branch หนึ่งเข้าอีก branch) หรือ push (ส่งโค้ดขึ้น GitHub) เข้าmain - หนึ่งในขั้นของ CI คือ
bin/rails db:test:prepare test= รัน test ทั้งหมด · test ไม่ผ่าน = GitHub ขึ้นกากบาทแดง (การเขียน test คือ Day 4) - gem ที่ดักให้จริง —
Gemfileมีstrong_migrations(บรรทัด 70) ที่หยุด migration อันตรายได้ทันที = เวอร์ชันบังคับของกฎข้อ 7 (รู้จักแล้วในบท 109) · และbullet(บรรทัด 72) ที่เตือนเรื่อง N+1 = ผู้ช่วยของกฎข้อ 5 (ใช้จริงในบท 125)
เทียบกับ TypeScript: comment ที่เขียนว่า // ห้ามส่ง null มาตรงนี้ คือคำแนะนำ — คนอ่านอาจข้าม ·
ส่วน type ที่ไม่รับ null แล้ว tsc ฟ้อง error (บท 63–68) คือการบังคับ · CLAUDE.md อยู่ฝั่ง comment · test/CI อยู่ฝั่ง type
ป้าย "กรุณาล้างมือก่อนเข้าครัว" ช่วยได้เยอะ คนส่วนใหญ่ทำตาม · แต่วันที่ยุ่ง ๆ ก็มีคนลืม ·
ถ้าเรื่องนี้พลาดไม่ได้จริง ร้านจะติดประตูที่เปิดเมื่อสแกนมือที่ล้างแล้วเท่านั้น — ใครจะลืมก็เข้าไม่ได้
CLAUDE.md = ป้าย · test/CI/gem ที่ดัก = ประตูที่ล็อก
ที่ไม่เหมือนกัน: ประตูจริงกันได้ทันทีที่หน้าครัว · test/CI ส่วนใหญ่จับได้หลังโค้ดถูกเขียนแล้ว (ตอนรัน test · ตอน push) ไม่ได้หยุด AI ตอนพิมพ์ · และประตูจริงตรวจได้แค่เรื่องเดียว (ล้างมือ) — test ตรวจได้เฉพาะเรื่องที่เราเขียน test ไว้ เรื่องที่ไม่มี test ก็กลับไปพึ่งป้ายเหมือนเดิม
- guidance vs enforcement
- guidance = คำแนะนำที่หวังให้ทำตาม (CLAUDE.md · comment) · enforcement = กลไกที่ไม่ผ่านก็ไปต่อไม่ได้ (test · CI · type checker · gem ที่ดัก) · ถาม senior ได้ว่า "กฎนี้สำคัญพอจะต้องมี test/CI จับไหม หรือแค่เขียนไว้ใน CLAUDE.md พอ"
- CI (continuous integration)
- ระบบที่รันตรวจโค้ดอัตโนมัติทุกครั้งที่ push หรือเปิด pull request · repo นี้ใช้ GitHub Actions (บริการ CI ของ GitHub · ตั้งค่าใน
.github/workflows/ci.yml) · พิมพ์บอก AI ได้ว่า "this rule must not regress — write a test for it so CI fails if someone breaks it"
4 อย่างที่จะใช้ทั้งวันใน Claude Code
สไลด์หน้า 7 สรุปไว้ 4 อย่าง · ทุกอย่างพิมพ์ในช่องแชทของ Claude Code ที่เปิดจากโฟลเดอร์โปรเจกต์ (ผมตรวจคำสั่งกับเอกสารทางการแล้ว แต่ไม่ได้แคปหน้าจอมา — หน้าตาของคุณให้ยึดตามที่เห็นจริง)
1 · @ไฟล์ ในช่องพิมพ์ — ยื่นคู่มือหน้านั้นให้ตอนสั่งงาน
> @prompts/03-hotwire.md ทำตามไฟล์นี้
>= ช่องพิมพ์ของ Claude Code (ไม่ต้องพิมพ์ตาม) · ไฟล์03-hotwire.mdยังไม่มีตอนนี้ — เกิดในบท 135- พิมพ์
@แล้วจะมีรายการไฟล์ขึ้นมาให้เลือก · กด Tab หรือ Enter เพื่อเลือก แล้วพิมพ์คำสั่งต่อ · เนื้อไฟล์ถูกใส่เข้าบทสนทนาครั้งนี้ - คนละเรื่องกับ
@ใน CLAUDE.md · ในช่องพิมพ์ = ยื่นให้ดูเฉพาะงานนี้ · ใน CLAUDE.md = import ที่โหลดทุก session · เทียบร้าน: แฟ้มที่แขวนข้างป้าย ทุกคนอ่านทุกกะ · ส่วน@ในช่องพิมพ์ = หัวหน้ายื่นคู่มือหน้านั้นใส่มือตอนสั่งงานนี้
2 · /context — เช็กว่ากฎโหลดมาจริงไหม
context (ของทั้งหมดที่ AI เห็นใน session) มีที่จำกัด เหมือนโต๊ะทำงานที่วางแฟ้มได้จำนวนหนึ่ง ·
/context แสดงว่าตอนนี้อะไรวางอยู่บนโต๊ะบ้าง · ให้หาหัวข้อ Memory files · สไลด์บอกว่าควรเห็น CLAUDE.md และ prompts/00-context.md ที่ถูก import เข้ามา (ผมไม่ได้เปิดหน้าจอนี้เอง ชื่อที่โชว์ของคุณอาจไม่ตรงเป๊ะ) ·
ถ้ารายการถูกพับไว้ เอกสารบอกให้พิมพ์ /context all
ไม่เห็น = เปิด claude ผิดโฟลเดอร์ หรือที่อยู่ไฟล์หลัง @ สะกดผิด · สไลด์บอกว่าเช็ก 5 วินาทีแต่กันงงได้ครึ่งชั่วโมง
3 · เติมกฎใหม่หลังเจอ AI พลาด — พิธีกรรมท้ายทุกบล็อก
> เพิ่มกฎนี้ลง prompts/00-context.md:
...กฎที่เพิ่งเจอ...
- ระบุชื่อไฟล์เสมอ · เอกสารบอกว่าถ้าพูดแค่ "จำไว้นะ" Claude อาจไปจดใน auto memory (โน้ตที่ Claude จดเองเก็บในเครื่องเรา ไม่เข้า git — เพื่อนในทีมไม่เห็น)
/memory= เปิดรายการไฟล์คำสั่ง (CLAUDE.md · CLAUDE.local.md · ฯลฯ) เลือกแล้วเปิดแก้ในเอดิเตอร์ได้ · และเปิดดู/ปิด auto memory ได้- หรือเปิด
prompts/00-context.mdใน VS Code แก้ด้วยมือก็ได้ — ผลเหมือนกัน · แก้แล้ว commit ทีมจะได้กฎใหม่ไปด้วย
4 · ของที่ควรรู้ไว้
| เรื่อง | ภาษาคน |
|---|---|
| เก็บไฟล์ให้สั้น (≤ ~200 บรรทัด) | ทุกบรรทัดกินพื้นที่ความจำของ AI ทุก session · เอกสารบอกว่าไฟล์ยาว = ทำตามได้แย่ลง และไฟล์ที่ import ก็นับรวมด้วย · ตอนนี้ CLAUDE.md 11 บรรทัด + 00-context 35 บรรทัด ยังสบาย |
/compact |
สรุปบทสนทนายาว ๆ ให้สั้นลงเพื่อคืนพื้นที่ · หลังสรุป CLAUDE.md ที่ root ถูกอ่านใหม่จากดิสก์ แต่คำสั่งที่พิมพ์ในแชทอาจหายไป — กฎที่อยากให้อยู่ถาวรต้องอยู่ในไฟล์ |
| อย่า import ไฟล์รายบล็อกเข้า CLAUDE.md | ถ้าใส่ @prompts/01-views.md ไว้ใน CLAUDE.md มันจะโหลดทุก session แม้กำลังทำเรื่องอื่น · เปลืองพื้นที่ และกฎของงานหนึ่งไปปนอีกงาน · ไฟล์รายบล็อกให้เรียกด้วย @ ในช่องพิมพ์ตอนจะใช้ |
claude --version |
พิมพ์ใน terminal (ไม่ใช่ในแชท) · เครื่องผมได้ 2.1.318 (Claude Code) ของคุณเป็นเลขอื่นได้ · เอกสารบางหัวข้อเขียนว่า "ต้องเวอร์ชันนี้ขึ้นไป" |
ถ้าจะใช้กับโปรเจกต์ที่ทำงาน: เปิด claude ในโฟลเดอร์โปรเจกต์ แล้วพิมพ์ /init · เอกสารบอกว่ามันจะสแกน repo แล้วร่าง CLAUDE.md ให้ (ถ้ามีอยู่แล้วจะเสนอแก้แทนการเขียนทับ) ·
ร่างที่ได้คือจุดเริ่ม — กฎที่ AI เดาจากโค้ดไม่ได้ (เช่นกฎธุรกิจ) เรายังต้องเติมเอง
- @-mention (file reference)
@ไฟล์ในช่องพิมพ์ = ใส่เนื้อไฟล์เข้าบทสนทนานี้ · ต่างจาก import ใน CLAUDE.md ที่โหลดทุก session · พิมพ์บอก AI ได้ว่า "follow @prompts/01-views.md and report against its acceptance criteria"- compaction ·
/compact - การย่อบทสนทนาให้สั้นลงเมื่อพื้นที่ใกล้เต็ม · ของในไฟล์โหลดกลับมา ของในแชทอาจหาย · ถาม senior ได้ว่า "หลัง compact แล้ว AI ลืมกฎ ควรย้ายกฎนั้นไปไว้ใน CLAUDE.md ไหม"
ภารกิจ
🎯 ภารกิจ (~10 นาที) — ฝึก 4 ส่วน + กฎส่วนตัวไฟล์แรก
ทำในโฟลเดอร์โปรเจกต์ wongnok-blog-all-in-one (ต่อจากบท 120):
- ดูของจริงสองอย่าง — พิมพ์ทีละบรรทัด:
ควรเห็นบรรทัดของ CLAUDE.md ขึ้นต้นด้วยls -l CLAUDE.md AGENTS.md wc -l prompts/00-context.mdlและมี-> AGENTS.md· และเลข35หน้าชื่อไฟล์ 00-context.md - ร่าง 4 ส่วน (เขียนในโน้ต ยังไม่ต้องสร้างไฟล์) — ตอนนี้หน้า
/postsโชว์หมวดเป็นตัวเลข เพราะapp/views/posts/_post.html.erbเขียน<%= post.category_id %>· ร่างคำสั่งงานให้ AI แก้เป็นชื่อหมวด โดยมีครบ 4 หัวข้อ: เป้าหมาย · ข้อกำหนด · ห้าม · เกณฑ์ตรวจรับ (อย่างน้อยหัวข้อละ 1 ข้อ) · ใบ้: กฎข้อ 5 ของ 00-context เกี่ยวตรง ๆ - สร้างกฎส่วนตัวไฟล์แรก — ใน VS Code สร้างไฟล์ใหม่ชื่อ
CLAUDE.local.mdที่ root (ระดับเดียวกับGemfile) แล้วใส่ของที่ใช้เฉพาะโปรเจกต์นี้ และเฉพาะคุณ 1 ข้อ เช่น:
ส่วนนิสัยที่ใช้ทุกโปรเจกต์ (เช่น "ตอบเป็นภาษาไทย") ควรไปอยู่# กฎส่วนตัวของฉัน (ไม่เข้า git) - โปรเจกต์นี้ฉันใช้เรียน Rails Day 3 — ก่อนแก้ไฟล์ ให้บอกว่าจะแก้บรรทัดไหนและทำไม~/.claude/CLAUDE.mdแทน · ไม่ต้องใส่เลขข้อ — เลข 9–12 จะเป็นของกฎทีมใน 00-context.md (บท 135) - พิสูจน์ว่า git ไม่เห็นไฟล์นี้:
git check-ignore -v CLAUDE.local.md git status --short - (ไม่บังคับ ถ้ามี Claude Code) เปิด
claudeในโฟลเดอร์นี้ พิมพ์/contextแล้วดูว่า Memory files มีครบสามไฟล์ไหม:CLAUDE.md·prompts/00-context.md·CLAUDE.local.md
ควรเห็น (ข้อ 4): บรรทัดแรกพิมพ์ออกมาว่า .gitignore:40:CLAUDE.local.md ตามด้วยชื่อไฟล์อีกรอบ = "ถูกกันไว้โดยบรรทัด 40 ของ .gitignore" · บรรทัดที่สอง ไม่มี CLAUDE.local.md โผล่
ถ้าเจออาการนี้ แปลว่า…
| เห็นอะไร | แปลว่า |
|---|---|
git check-ignore -v เงียบ ไม่พิมพ์อะไรเลย · และ git status --short โชว์ ?? CLAUDE.local.md |
.gitignore ไม่ได้กันไฟล์นี้ (เช่นโปรเจกต์อื่นที่ไม่ใช่ของคอร์ส) · ?? = "ไฟล์ใหม่ที่ git ยังไม่เคยเก็บ" ถ้า git add . จะหลุดเข้า commit ·
แก้: เพิ่มบรรทัด CLAUDE.local.md ท้าย .gitignore แล้วรันข้อ 4 ซ้ำ · ผมลองลบบรรทัดนั้นทิ้งดูแล้ว ได้อาการนี้จริงทั้งสองอย่าง |
ls -l ได้ CLAUDE.md ขึ้นต้นด้วย - และไม่มีลูกศร |
CLAUDE.md เป็นไฟล์ธรรมดา ไม่ใช่ symlink — มักเกิดบน Windows ที่ไม่ได้เปิดการรองรับ symlink · เปิดดูด้วย cat CLAUDE.md ถ้าเห็นแค่คำว่า AGENTS.md บรรทัดเดียว แปลว่าเป็นอาการนี้ (Claude Code จะไม่เห็นกฎเลย) ·
แก้ในเครื่องตัวเอง: เปลี่ยนบรรทัดนั้นเป็น @AGENTS.md ซึ่งเป็นท่าที่เอกสารแนะนำสำหรับ Windows |
No such file or directory |
ไม่ได้อยู่ในโฟลเดอร์โปรเจกต์ → cd เข้าโฟลเดอร์ wongnok-blog-all-in-one ก่อน |
ข้อ 1 และ 4 — ผลรันจริง (ชื่อเจ้าของและวันเวลาของคุณจะต่าง):
$ ls -l CLAUDE.md AGENTS.md
-rw-r--r--@ 1 tuwanon wheel 389 Nov 7 09:14 AGENTS.md
lrwxr-xr-x@ 1 tuwanon wheel 9 Nov 7 11:18 CLAUDE.md -> AGENTS.md
$ wc -l prompts/00-context.md
35 prompts/00-context.md
$ git check-ignore -v CLAUDE.local.md
.gitignore:40:CLAUDE.local.md CLAUDE.local.md
$ git status --short
$
บรรทัด git status --short ไม่พิมพ์อะไรเลย = ไม่มีไฟล์ไหนเปลี่ยน และ CLAUDE.local.md ถูกซ่อนไว้แล้ว (ถ้าคุณยังไม่ได้ commit db/seeds.rb จากบท 120 จะเห็นบรรทัด M db/seeds.rb — ปกติ)
ข้อ 2 — ตัวอย่างร่างหนึ่งแบบ (ผมเขียนเอง ไม่ได้ส่งให้ AI ทำ · ของคุณใช้คำอื่นได้ ขอแค่ครบ 4 ส่วนและตรวจได้จริง):
## เป้าหมาย
หน้ารายการบทความ (/posts) บอกชื่อหมวดของแต่ละบทความ เช่น "ของหวาน" แทนตัวเลข
## ข้อกำหนด
- แก้ app/views/posts/_post.html.erb ให้แสดง post.category.name
- PostsController#index ต้อง includes(:category)
## ห้าม
- แก้ไฟล์อื่นนอกจากสองไฟล์ข้างบน
- เปลี่ยนโครงตาราง (ไม่ต้องมี migration)
## เกณฑ์ตรวจรับ
- [ ] เปิด /posts แล้วเห็นชื่อหมวดเป็นภาษาไทย ไม่มีตัวเลข category_id
- [ ] log ของการโหลด /posts ครั้งเดียว ไม่มี SELECT ตาราง categories ซ้ำทีละบทความ
- ข้อกำหนด
includes(:category)มาจากกฎข้อ 5 · ถ้าไม่บอก AI อาจเขียนpost.category.nameเฉย ๆ แล้วได้ N+1 (บท 114) - เกณฑ์ตรวจรับข้อ 2 คือวิธีพิสูจน์ด้วยตาตัวเอง — ไม่ใช่เชื่อที่ AI บอกว่า "แก้แล้ว" · การนับ query จาก log ทำจริงในบท 125
- งานนี้ (และมากกว่านี้) จะทำจริงใน Lab Vibe 1 บท 126 พร้อมไฟล์
prompts/01-views.mdฉบับเต็ม
เก็บกวาด: CLAUDE.local.md เก็บไว้ได้เลย (เป็นของส่วนตัว ไม่กระทบโค้ดบทต่อไป) · ถ้าไม่อยากเก็บ rm CLAUDE.local.md ·
ถ้าเผลอแก้ AGENTS.md หรือ 00-context.md ระหว่างทาง คืนค่าด้วย git restore AGENTS.md prompts/00-context.md
ทดสอบความเข้าใจ
1. อยากให้ Claude Code รู้ว่า DB ทดลองในเครื่องคุณอยู่พอร์ต 55432 (เพื่อนในทีมใช้พอร์ตอื่น) ควรเขียนไว้ที่ไหน?
ตัวเลือกที่ 3 ถูก · ค่านี้เป็นของโปรเจกต์นี้ และเป็นของเราคนเดียว — ตรงกับ CLAUDE.local.md พอดี · และ repo ของคอร์สกันไว้ใน .gitignore บรรทัด 40
ตัวเลือกที่ 1 และ 2 ผิด — สองไฟล์นี้เข้า git · พอร์ตของเราจะไปปนกับของเพื่อน AI ในเครื่องเพื่อนจะต่อผิดพอร์ต · ตัวเลือกที่ 4 ผิด — ไม่หลุดเข้า git จริง แต่ไฟล์นี้ใช้กับทุกโปรเจกต์ โปรเจกต์อื่นไม่ได้ใช้พอร์ตนี้
จุดที่ต้องจำ: ถามสองคำถาม — ใช้กับโปรเจกต์ไหน (ทุกอัน/อันนี้) · ใครต้องรู้ (ทั้งทีม/เราคนเดียว) · แล้วจะรู้เองว่าไฟล์ไหน
2. เครื่องหมาย @ ในสองที่นี้ต่างกันยังไง: บรรทัด @prompts/00-context.md ใน CLAUDE.md กับการพิมพ์ @prompts/01-views.md ทำตามไฟล์นี้ ในช่องแชท?
ตัวเลือกที่ 2 ถูก · @ ใน CLAUDE.md คือ import — เนื้อไฟล์ถูกโหลดตอนเริ่มทุก session · @ ในช่องแชทคือยื่นไฟล์ให้ดูเฉพาะงานนี้
ตัวเลือกที่ 1 ผิด — @ ในช่องแชทไม่ได้แก้ไฟล์ไหน จึงไม่มีอะไรติดไปถึง session หน้า เปิดแชทใหม่ก็ต้องพิมพ์ใหม่ ·
ตัวเลือกที่ 3 ผิด — เอกสารบอกว่าไฟล์ที่ import ถูกโหลดเข้ามาพร้อม CLAUDE.md เลย ไม่ได้รอ AI ตัดสินใจเปิด ·
ตัวเลือกที่ 4 ผิด — การพิมพ์ในแชทไม่ได้แก้ไฟล์ CLAUDE.md ให้ ถ้าอยากให้ถาวรต้องสั่งหรือแก้ไฟล์เอง
จุดที่ต้องจำ: ของที่ใช้ทุกงาน → import ใน CLAUDE.md · ของที่ใช้บางงาน → @ ในช่องแชทตอนจะใช้
3. 00-context.md มีกฎ "index action ต้องมี limit หรือ pagination" แต่ AI ยังเขียน @posts = Post.all มาให้ ข้อสรุปไหนถูกที่สุด?
ตัวเลือกที่ 1 ถูก · ทั้งสไลด์และเอกสารพูดตรงกันว่าไฟล์พวกนี้เป็นคำแนะนำ ไม่ใช่การบังคับ · หน้าที่ของเราคือติ๊กเกณฑ์ตรวจรับเอง และเรื่องที่พลาดไม่ได้ให้ test/CI จับ
ตัวเลือกที่ 2 ผิด — โหลดแล้วก็ยังพลาดได้ (เช็กว่าโหลดไหมด้วย /context ไม่ใช่เดาจากผลงาน) ·
ตัวเลือกที่ 3 ผิด — 46 บรรทัดยังห่างจาก ~200 มาก ลบกฎดี ๆ ทิ้งไม่ได้แก้ปัญหา ·
ตัวเลือกที่ 4 ผิด — เอกสารบอกว่าไฟล์ต่าง ๆ ถูกเอามาต่อกัน ไม่มีไฟล์ไหนชนะไฟล์ไหน และกฎทีมควรอยู่ที่ทีมเห็น
จุดที่ต้องจำ: CLAUDE.md = ป้าย · test/CI = ประตูที่ล็อก · AI ทำผิดกฎ = เรื่องปกติที่ต้องตรวจเจอ ไม่ใช่ระบบพัง
4. เพื่อนเสนอให้ใส่ @prompts/01-views.md · @prompts/02-styling.md · @prompts/03-hotwire.md ลงใน CLAUDE.md "AI จะได้รู้ทุกอย่างตลอด" ทำไมไม่ควรทำ?
ตัวเลือกที่ 4 ถูก · import = โหลดทุก session · ไฟล์รายบล็อกใช้เฉพาะตอนทำบล็อกนั้น จึงควรเรียกด้วย @ ในช่องแชทตอนจะใช้ · ไฟล์ที่ import ยังนับรวมในงบ ~200 บรรทัดด้วย
ตัวเลือกที่ 1 ผิด — import ได้หลายไฟล์ และไฟล์ถูกเอามาต่อกัน ไม่ทับกัน · ตัวเลือกที่ 2 ผิด — prompts/ เข้า git อยู่แล้วโดยตั้งใจ (ทีมใช้ร่วมกัน) · ตัวเลือกที่ 3 ผิด — ไฟล์ที่โหลดเป็นข้อมูลประกอบ AI ไม่ได้ลงมือทำเองจนกว่าเราจะสั่งงาน
จุดที่ต้องจำ: CLAUDE.md เก็บเฉพาะของที่จริงทุกงาน (stack · กฎถาวร · คำสั่งที่ใช้บ่อย) · ที่เหลือเรียกเมื่อใช้
ถามกลับได้เลย
ถ้าไม่แน่ใจว่ากฎข้อหนึ่งควรอยู่ไฟล์ไหน ให้ก็อปกฎข้อนั้นมาถาม พร้อมบอกว่า "ใช้กับโปรเจกต์ไหน · ใครต้องรู้" ·
หรือถ้า /context ไม่เห็นไฟล์ที่ควรเห็น ให้ก็อปผล ls -l CLAUDE.md AGENTS.md กับ pwd (โฟลเดอร์ที่เปิดอยู่) มาด้วย — สองอย่างนี้ตอบได้เกือบทุกครั้ง
ถ้าเปิดสไลด์ดูประกอบ
| หน้า | สไลด์เขียน | ของจริง (repo branch day3-start) | ต้องสนใจไหม |
|---|---|---|---|
| 4 | "ท้ายวันอังคาร" รวมเป็น FINAL.md · "ความรู้ 16 ชั่วโมง" | FINAL.md ยังไม่มีใน repo (เกิดท้ายคอร์ส) · ตัวเลขชั่วโมงเป็นคำของสไลด์ | ไม่ต้อง |
| 5 | Stack: Ruby 4.0.6 / Rails 8.1.3 / PostgreSQL 17 · tailwindcss-rails 4.6 | ไฟล์จริงเขียน Ruby 4.0.5 / Rails 8.1.3 / PostgreSQL 18 · ไม่ระบุเลขรุ่น tailwindcss-rails · มีบรรทัด "ห้ามสร้าง tailwind.config.js" เพิ่ม | ไม่ต้อง — ยึดไฟล์ใน repo |
| 5 | รูปแบบโค้ด 3 ข้อ · รูปแบบการตอบ 2 ข้อ · หัวข้อ "(จากวันพฤหัส)" | มี "ห้ามมี <script> ใน view" และ "อย่าเพิ่ง refactor อย่างอื่นที่ไม่ได้ขอ" เพิ่ม · หัวข้อเขียน "(จากวันก่อน)" · ถ้อยคำกฎ 8 ข้อต่างเล็กน้อย ความหมายเดิม |
ไม่ต้อง |
| 6 | CLAUDE.md มีคำสั่งที่ใช้บ่อย 3 บรรทัด | CLAUDE.md เป็น symlink ไป AGENTS.md · มีคำสั่ง 5 บรรทัด (เพิ่ม bin/rails routes · docker compose up -d) |
รู้ไว้ — แก้กฎให้แก้ AGENTS.md |
| 6 | "กฎทั้ง 12 ข้อของเรา" | ตอนนี้มี 8 ข้อ · ข้อ 9–12 เพิ่มในบท 135 | รู้ไว้ |
| 7 | /context แล้วดูหัวข้อ Memory files |
เอกสารบอกว่าบางโหมดแสดงผล /context จะพับรายการไว้ ต้องพิมพ์ /context all |
รู้ไว้ ถ้าไม่เห็นรายการ |
อ้างอิง
- Claude Code Docs — How Claude remembers your project —
อ่านตาราง Choose where to put CLAUDE.md files (ชั้นของไฟล์ · ใครเห็น) · ต่อด้วย Write effective instructions (ขนาด ~200 บรรทัด · กฎที่ขัดกัน) ·
และ Import additional files (การใช้
@path· ที่อยู่นับจากไฟล์ที่ import) - Claude Code Docs — AGENTS.md — ตารางว่าเมื่อไหร่อ่าน AGENTS.md เมื่อไหร่อ่าน CLAUDE.md · หัวข้อย่อย Share one file with other coding tools คือท่า symlink ที่ repo ของคอร์สใช้ และข้อควรระวังบน Windows
- Claude Code Docs — Commands —
หาแถว
/context·/compact·/init·/memoryในตาราง All commands ว่าแต่ละตัวทำอะไร - Claude Code Docs — Reference files and directories —
การใช้
@ไฟล์ในช่องพิมพ์ · ปุ่ม Tab/Enter ตอนเลือกไฟล์ - Claude Code Docs — What survives compaction —
ตารางสั้น ๆ ว่าหลัง
/compactอะไรถูกโหลดกลับ (CLAUDE.md ที่ root) อะไรหาย - repo ของคอร์ส — โฟลเดอร์ prompts/ ใน branch day3-start —
เปิด
00-context.mdฉบับต้นทาง · และไฟล์AGENTS.md·.gitignoreที่ root ของ branch เดียวกัน