🧰 ของที่ใช้ตรวจในบทนี้
Next.js 16.3.5 (Turbopack) # npx next --version → Next.js v16.3.5
react 19.2.8
typescript 5.9.3
eslint 9.39.5
tailwindcss 4.3.3
node v24.14.0
git 2.51.0
โปรเจกต์ที่ใช้ตรวจสร้างด้วย create-next-app + shadcn init (preset ที่ใช้ @base-ui/react)
+ shadcn add button card badge skeleton — หน้าตาเดียวกับที่ทำในบท 79–85
ทุกคำสั่งและทุก output ในบทนี้ก็อปมาจากหน้าจอจริง ·
ส่วน “แผน” ในขั้น Plan เป็นแผนที่ผมเขียนเองก่อนลงมือ ไม่ใช่คำตอบของ AI —
บทนี้ไม่ได้รัน Claude Code สักครั้ง เพราะผลของ AI ไม่ซ้ำกันทุกรอบ
เอามาโชว์เป็น “ผลรัน” ไม่ได้ · สิ่งที่บทนี้สอนคือขั้นตอน ซึ่งคุณเดินเองได้ และตรวจ agent ได้
Agentic workflow คืออะไร
สไลด์หน้า 12 นิยามไว้ประโยคเดียว:
แปลเป็นภาษาคน: ให้ AI ทำงานหลายขั้นต่อกันเองได้ —
ไม่ใช่ถามหนึ่งคำ ตอบหนึ่งก้อน แล้วคุณเอาไปก็อปวางเอง
แต่เป็น AI ที่คิด (reasoning) · วางแผน (planning) ·
หยิบเครื่องมือมาใช้เอง (tool use — เปิดไฟล์ · ค้นโค้ด · รันคำสั่งใน terminal)
แล้วปรับตัวตามผลที่ได้ (dynamically — รันแล้ว error ก็อ่าน error แล้วแก้ต่อ)
สไลด์หน้าเดียวกันวาดขั้นตอนนั้นไว้เป็น 5 กล่องเรียงกัน เริ่มจากคำขอของผู้ใช้
(ตัวอย่างในสไลด์คือ "Add dark mode to the app") แล้วมีเส้นประย้อนกลับจากขั้นที่ 4:
เส้นล่างสุดคือหัวใจของทั้งบท — Evaluate ไม่ผ่าน ห้ามเดินหน้าไป Commit ต้องย้อนกลับไปแก้ แล้วตรวจใหม่ทั้งชุด วนจนกว่าจะผ่าน
ภาพในสไลด์หน้า 12 มีหัวเรื่องว่า “AGENTIC WORKFLOW by Claude Code” พร้อมคำโปรยว่า “Claude Code works like an agent that understands your goal, thinks, acts, and delivers production-ready changes.” — Claude Code ทำงานเหมือน agent ที่เข้าใจเป้าหมาย คิด ลงมือ แล้วส่งงานที่พร้อมใช้จริง · แต่ละกล่องมีคำสรุปสั้น ๆ ใต้ชื่อขั้น (ถอดตามจริง):
| ขั้น | คำสรุปใต้ชื่อกล่อง | แปล |
|---|---|---|
| 1 EXPLORE | Understand the codebase and context | เข้าใจโค้ดและบริบท |
| 2 PLAN | Break down the task and create a plan | แตกงานแล้ววางแผน |
| 3 CODE | Implement the plan | ลงมือตามแผน |
| 4 EVAL | Evaluate the changes and results | ประเมินสิ่งที่เปลี่ยนและผลลัพธ์ |
| 5 COMMIT | Deliver clean, reviewable changes | ส่งงานที่สะอาด รีวิวได้ |
แถวล่างของภาพชื่อ WHY THIS WORKS สรุปว่าทำไมห้าขั้นนี้ได้ผล:
Context First (Understand deeply before changing anything) ·
Planned Execution (Reduce mistakes by thinking ahead) ·
Iterative Improvement (Continuously refine until it's right) ·
Quality Focused (Evaluation ensures reliability and maintainability) ·
Transparent & Reviewable (Clear plan, clean diff, meaningful commits)
และปิดท้ายว่า “Explore → Plan → Code → Eval → Commit — Claude Code follows this agentic workflow to ship better software, faster.”
สไลด์หน้า 17 เขียนว่า “If FAILURES here should be loop back to Code” — ย้อนไป Code
สไลด์หน้า 12 (ภาพ diagram) ลากเส้นประ NO ไปสองที่ คือกล่อง Plan และกล่อง Code
พร้อมคำว่า “Return to the appropriate step and iterate”
สองหน้านี้ไม่ได้ขัดกัน หน้า 17 พูดถึงกรณีที่พบบ่อยที่สุด (โค้ดผิด → แก้โค้ด)
ส่วนหน้า 12 ครอบกรณีที่แผนผิดตั้งแต่ต้นด้วย — แก้โค้ดกี่รอบก็ไม่ผ่าน เพราะทางที่เลือกไม่ถูก ต้องกลับไปวางแผนใหม่
ในบทนี้ผมยึดแบบหน้า 12 เพราะครบกว่า
นึกถึงช่างซ่อมบ้านที่ดี ที่คุณเรียกมาเพราะ “ก๊อกในครัวน้ำหยด”
ช่างดีจะไม่ถอดก๊อกทันที — เขาเดินดูก่อน (ท่อเป็นแบบไหน ยี่ห้ออะไร วาล์วปิดน้ำอยู่ตรงไหน) →
บอกคุณก่อนว่าจะทำอะไร (“เปลี่ยนซีลยาง ไม่ต้องเปลี่ยนทั้งก๊อก ค่าของ 80 บาท”) →
ลงมือตามที่บอก → เปิดน้ำทดสอบ ดูว่ายังหยดไหม ตรงอื่นรั่วเพิ่มไหม →
ผ่านแล้วค่อยเขียนใบงานส่งมอบว่าทำอะไรไป
ห้าขั้นนั้นคือ Explore · Plan · Code · Evaluate · Commit เป๊ะ ๆ
ที่ไม่เหมือนกัน: ช่างจริงเปิดน้ำแล้วเห็นด้วยตาทันทีว่ายังหยดไหม
แต่โค้ด “ดูเหมือนเสร็จ” ได้ทั้งที่ยังพัง — ปุ่มขึ้นจอสวยแต่ type ผิด · build ผ่านแต่ไม่ตรงโจทย์
และช่างจริงเหนื่อยเป็น จึงไม่อยากทำซ้ำ · agent ไม่เหนื่อย จึงพร้อมจะเดินต่อเรื่อย ๆ แม้ทางผิด
ถ้าไม่มีขั้นตรวจที่ให้ผล “ผ่าน/ไม่ผ่าน” แบบชัด ๆ มันจะหยุดตอนที่คิดว่าเสร็จ ไม่ใช่ตอนที่เสร็จจริง
- agent / agentic
- ระบบ AI ที่ไม่ได้แค่ตอบข้อความ แต่ลงมือทำหลายขั้นเอง ตัดสินใจเองว่าขั้นต่อไปคืออะไร · Claude Code คือ coding agent — มันเปิดไฟล์ รันคำสั่ง แก้โค้ดในเครื่องคุณได้ · ถาม senior ได้ว่า “งานนี้ให้ agent ทำทั้งวงได้ไหม หรือควรคุมทีละขั้น”
- tool use
- การที่ AI เรียกใช้เครื่องมือภายนอก เช่น อ่านไฟล์ · ค้นโค้ด · รัน
npx tsc· นี่คือสิ่งที่ทำให้ agent “เห็นผลจริง” ได้ ไม่ต้องเดา · พิมพ์บอก AI ได้ว่า “run the type-check yourself and show me the output” - feedback loop / iterate
- วงจร “ทำ → ตรวจ → เอาผลตรวจมาแก้ → ตรวจใหม่” · คำว่า iterate ในสไลด์หมายถึงการวนรอบนี้ · ประโยคที่ใช้บ่อย: “iterate until tsc, eslint and next build all pass”
ขั้น 1 · Explore — เก็บบริบทก่อนแตะอะไร
สไลด์หน้า 14 มีสามข้อ (ถอดตามจริง):
- “Like a researcher, gather context before touching anything.” — ทำตัวเป็นนักวิจัย เก็บบริบทก่อนแตะอะไรทั้งนั้น
- “Read the relevant files, search the codebase, understand existing patterns, conventions, and dependencies.” — อ่านไฟล์ที่เกี่ยว ค้นทั้งโปรเจกต์ ทำความเข้าใจแพตเทิร์น ธรรมเนียมการเขียน และของที่พึ่งพากันอยู่
- “The goal is a mental model of what exists and how it works, not writing code yet.” — เป้าหมายคือภาพในหัวว่ามีอะไรอยู่แล้วและมันทำงานยังไง ยังไม่ใช่การเขียนโค้ด
diagram หน้า 12 แตกเป็นรายการงานย่อย: Read README and docs · Explore project structure · Search for relevant files and code · Understand existing patterns and conventions · Review tests and configs · เป้าหมาย: Build a mental model of the project.
ลองกับงานจริง: “เพิ่มปุ่มสีเขียวสำหรับสถานะสำเร็จ”
ก่อนแก้อะไร ผมเปิดโปรเจกต์ดูสี่คำถาม — Button อยู่ไหน · variant ที่มีอยู่แล้วเขียนแบบไหน · สีมาจากไหน · มีใครใช้ Button อยู่แล้วบ้าง (จะได้รู้ว่าแก้แล้วกระทบใคร):
$ ls components/ui
badge.tsx
button.tsx
card.tsx
skeleton.tsx
$ grep -n "variant" components/ui/button.tsx
8: variants: {
9: variant: {
36: variant: "default",
44: variant = "default",
51: className={cn(buttonVariants({ variant, size, className }))}
$ grep -n "destructive" app/globals.css
29: --color-destructive: var(--destructive);
66: --destructive: oklch(0.577 0.245 27.325);
101: --destructive: oklch(0.704 0.191 22.216);
$ grep -rn "components/ui/button" app components; echo "rc=$?"
rc=1
สี่บรรทัดคำสั่งนี้ให้ภาพในหัวครบพอจะวางแผนได้:
- variant ของ Button เป็นรายการใน
cva(...)— มีdefault · outline · secondary · ghost · destructive · linkแต่ละตัวคือสตริง class ของ Tailwind · แปลว่าเพิ่ม variant = เพิ่มหนึ่งบรรทัดในรายการนี้ - สีไม่ได้เขียนตรง ๆ เช่น
bg-red-600แต่ใช้ชื่อสี (token) อย่างbg-destructiveซึ่งไปโยงกับตัวแปร CSS ในapp/globals.cssสองชุด — บรรทัด 66 สำหรับโหมดสว่าง · บรรทัด 101 สำหรับโหมดมืด · นี่คือ convention ของโปรเจกต์ ถ้าเราเขียนbg-green-600ตรง ๆ โค้ดก็ทำงาน แต่จะผิดธรรมเนียม และโหมดมืดจะไม่เปลี่ยนสีตาม rc=1ของgrepแปลว่าไม่เจอเลย — ยังไม่มีไฟล์ไหน import Button · เพิ่ม variant ใหม่จึงไม่มีทางทำหน้าเดิมพัง
และอีกสองไฟล์ที่ควรเปิดในขั้นนี้เสมอ: package.json (สคริปต์ที่มีคือ
dev · build · start · lint — ไม่มี test
ขั้น Evaluate จึงต้องพึ่ง type-check · lint · build) และ AGENTS.md ที่
create-next-app รุ่นนี้สร้างมาให้ ซึ่งเขียนเตือน agent ไว้ตรง ๆ ว่า
“This is NOT the Next.js you know” ให้ไปอ่านเอกสารใน node_modules/next/dist/docs/ ก่อนเขียนโค้ด
— ไฟล์นี้มีไว้ให้ agent อ่านในขั้น Explore โดยเฉพาะ
# คำขอตัวอย่างในสไลด์หน้า 12
"Add dark mode to the app"
# ถ้า agent ข้าม Explore แล้วเริ่มเขียนโค้ดทันที ...
ในโปรเจกต์ shadcn แบบเดียวกับบท 79–85 — agent ที่ไม่ Explore จะพลาดอะไรไป?
ผมรัน Explore จริงบนโปรเจกต์นั้น:
$ grep -n "dark" app/globals.css
5:@custom-variant dark (&:is(.dark *));
86:.dark {
$ grep -c "dark:" components/ui/button.tsx
4
$ grep -rn "next-themes" package.json; echo "rc=$?"
rc=1
โปรเจกต์นี้มี dark mode อยู่แล้วครึ่งทาง — globals.css มีชุดสีโหมดมืดทั้งก้อน
(บรรทัด 86 .dark { ... }) และ Button มี class dark: อยู่ 4 บรรทัด
ที่ยังไม่มีคือตัวสลับโหมด (ไม่มีแพ็กเกจอย่าง next-themes) และไม่มีใครใส่ class dark ให้ <html>
agent ที่ข้าม Explore มีโอกาสสูงที่จะเขียนระบบสีโหมดมืดขึ้นใหม่ทั้งชุด
ซ้อนกับของเดิม — โค้ดอาจ build ผ่านด้วย แต่ตอนนี้โปรเจกต์มีสองระบบที่ตีกันเอง
ส่วน agent ที่ Explore ก่อนจะรู้ว่างานจริงเล็กกว่าที่คิดมาก:
“ทำปุ่มสลับ แล้วใส่/ถอด class dark ที่ <html>”
· คำขอเดียวกัน แต่งานคนละขนาด — รู้ได้จากการอ่านเท่านั้น
ขั้น 2 · Plan — ตัดสินใจก่อนลงมือ และให้คนรีวิวได้
สไลด์หน้า 15 (ถอดตามจริง):
- “Decide the approach before implementing.” — เลือกวิธีให้ได้ก่อนลงมือ
- “Break the task into steps, identify which files change, and anticipate edge cases and trade-offs.” — แตกงานเป็นขั้น · ระบุว่าไฟล์ไหนจะเปลี่ยน · คาดการณ์ edge case และสิ่งที่ต้องแลก
- “A good plan is reviewable by someone who can catch a bad direction before written code.” — แผนที่ดีคือแผนที่คนอื่นอ่านแล้วจับได้ว่าไปผิดทาง ตั้งแต่ก่อนมีโค้ดสักบรรทัด
diagram หน้า 12: Identify what needs to be changed · Determine affected files and dependencies · Consider edge cases and impacts · Create a step-by-step implementation plan · เป้าหมาย: Have a clear, safe plan before writing code.
นี่คือแผนที่ผมเขียนจริงก่อนแตะโค้ด (เขียนเอง ไม่ใช่ output ของ AI):
สังเกตว่าแผนนี้สั้นพอให้คนอ่านจบในหนึ่งนาที และมีจุดที่ reviewer แย้งได้จริง — เช่น senior อาจบอกว่า “ไม่ต้องทำโหมดมืดตอนนี้ ทีมยังไม่เปิดใช้” ซึ่งจะตัดงานไปครึ่งหนึ่ง ตั้งแต่ก่อนมีโค้ด — นี่คือความหมายของ reviewable ในสไลด์
เอกสารทางการของ Claude Code พูดเรื่องเดียวกันนี้ไว้ ในหัวข้อ
Explore first, then plan, then code — แนะนำให้ใช้ plan mode
(กด Shift+Tab จนแถบสถานะขึ้น ⏸ plan mode on) ซึ่ง agent จะอ่านไฟล์และเสนอแผนได้
แต่แก้ไฟล์ไม่ได้จนกว่าคุณจะอนุมัติ
และเอกสารเดียวกันก็เตือนไว้ด้วยว่า ถ้างานเล็กจนอธิบาย diff ได้ในประโยคเดียว
(แก้คำผิด · เพิ่ม log หนึ่งบรรทัด) ข้ามการวางแผนได้ — แผนมีต้นทุน ใช้เมื่อไม่แน่ใจวิธี หรือแก้หลายไฟล์
ขั้น 3 · Code — ทำตามแผน ไม่ด้นสด
สไลด์หน้า 16 (ถอดตามจริง):
- “Execute the plan” — ลงมือตามแผน
- “Edits by following the conventions found during explore step” — แก้โค้ดตามธรรมเนียมที่เจอตอน Explore
- “Implement what was planned rather than improvising a new scope mid-stream.” — ทำสิ่งที่วางแผนไว้ ไม่ด้นขอบเขตงานใหม่ขึ้นมากลางทาง
diagram หน้า 12: Write or edit code · Create / update components or files · Run tests and linters · Fix issues and iterate · Refactor if needed · เป้าหมาย: Working code that implements the plan.
นี่คือ diff จริงของสองไฟล์หลัก (ไฟล์ที่สาม globals.css เพิ่ม token 6 บรรทัดตามแผน):
$ git diff components/ui/button.tsx app/page.tsx
diff --git a/app/page.tsx b/app/page.tsx
index c887311..6d5057b 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,4 +1,5 @@
import Image from "next/image";
+import { Button } from "@/components/ui/button";
export default function Home() {
return (
@@ -54,6 +55,7 @@ export default function Home() {
/>
Deploy Now
</a>
+ <Button variant="success">บันทึกแล้ว</Button>
<a
className="flex h-12 w-full items-center ... md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&..."
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
index 997e0c7..e4aebd8 100644
--- a/components/ui/button.tsx
+++ b/components/ui/button.tsx
@@ -16,6 +16,7 @@ const buttonVariants = cva(
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted ...",
destructive:
"bg-destructive/10 text-destructive hover:bg-destructive/20 ...",
+ success: "bg-success text-success-foreground hover:bg-success/80",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
(บรรทัดบริบทที่เป็นสตริง class ยาว ๆ สี่บรรทัด ผมตัดกลางบรรทัดเป็น ... ให้พอดีจอ ·
บรรทัดที่ขึ้นต้นด้วย + ไม่ได้ตัดสักตัวอักษร)
บรรทัดที่เพิ่มจริงมีแค่ 9 บรรทัด ใน 3 ไฟล์ ตรงกับแผน ·
สิ่งที่ไม่ได้ทำก็สำคัญพอกัน: หน้า page.tsx ของ create-next-app
ยังมีข้อความ “To get started, edit the page.tsx file” อยู่ ชวนให้ลบทิ้งมาก —
แต่มันไม่อยู่ในแผน ถ้าลบในรอบนี้ diff จะบวมเป็นหลายสิบบรรทัด แล้วคนรีวิวจะหาบรรทัดสำคัญไม่เจอ
เจอของที่อยากแก้ระหว่างทาง → จดไว้เป็นงานถัดไป ไม่ใช่ยัดเข้ามาในงานนี้
ขั้น 4 · Evaluate — ตรวจว่าถูกจริง ไม่ใช่ดูเหมือนถูก
สไลด์หน้า 17 (ถอดตามจริง):
- “Verify the work is CORRECT — Run tests, lint, type-check, execute the code, and review the diff.”
- “If FAILURES here should be loop back to Code”
“Should check the original requirements.”
— ต้องย้อนกลับไปเทียบกับโจทย์ตั้งต้นด้วย · เครื่องมือทุกตัวผ่านหมด ไม่ได้แปลว่าทำสิ่งที่ถูกขอ
diagram หน้า 12: Run tests · Check linting / types · Validate requirements · Review edge cases · Ensure no regressions · Assess quality · เป้าหมาย: High-quality, reliable changes.
รอบที่ 1 — ไม่ผ่าน
ในรอบแรกผมจงใจพิมพ์ผิด variant="sucess" (ขาด c ตัวหนึ่ง)
เพื่อดูว่าเครื่องมือแต่ละตัวจับได้ไหม
(diff ในขั้น Code ข้างบนคือฉบับหลังแก้ในรอบที่ 2 แล้ว · รอบนี้ต่างกันแค่ตัวอักษรนั้นตัวเดียว) — ผลจริง:
$ npx tsc --noEmit; echo "rc=$?"
app/layout.tsx(20,50): error TS2304: Cannot find name 'LayoutProps'.
app/page.tsx(58,19): error TS2820: Type '"sucess"' is not assignable to type '"link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "success" | null | undefined'. Did you mean '"success"'?
rc=2
$ npx eslint .; echo "rc=$?"
rc=0
สองเรื่องในผลนี้ที่ต้องหยุดอ่าน:
eslintผ่าน (rc=0) ทั้งที่โค้ดผิด — linter ดู “สไตล์และแพตเทิร์นอันตราย” ไม่ได้ตรวจ type · ถ้าตรวจแค่ lint แล้วเชื่อ คุณจะ commit ตัวพิมพ์ผิดนี้ไปเลย นี่คือเหตุผลที่สไลด์ใส่ lint กับ type-check แยกกันเป็นสองอย่าง- error บรรทัดแรก (
LayoutProps) ไม่ได้มาจากที่เราแก้ —LayoutPropsเป็น type ที่ Next.js สร้างให้ไว้ในโฟลเดอร์.next/types/ตอนรันnext dev/next build· โปรเจกต์ที่เพิ่ง clone มาและยังไม่เคยรันจะยังไม่มีโฟลเดอร์นี้ · สร้างได้โดยไม่ต้อง build ทั้งโปรเจกต์:
$ npx next typegen
Generating route types...
✓ Types generated successfully
$ npx tsc --noEmit; echo "rc=$?"
app/page.tsx(58,19): error TS2820: Type '"sucess"' is not assignable to type '"link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "success" | null | undefined'. Did you mean '"success"'?
rc=2
ตอนนี้เหลือ error ที่เป็นของเราจริง ๆ ตัวเดียว — และ TypeScript บอกคำตอบมาให้เลย
(Did you mean '"success"'?) เพราะ cva สร้าง type ของ variant
จากรายการที่เราเขียนใน button.tsx · ลองดูว่า next build พูดว่าอะไร:
$ npx next build
▲ Next.js 16.3.5 (Turbopack)
✓ Running next.config.ts took 12ms
Creating an optimized production build ...
✓ Compiled successfully in 3.0s
Running TypeScript ...
app/page.tsx(58,19): error TS2820: Type '"sucess"' is not assignable to type '"link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "success" | null | undefined'. Did you mean '"success"'?
Failed to type check.
สังเกตบรรทัด ✓ Compiled successfully ที่ขึ้นเครื่องหมายถูกก่อนจะพังตอน
Running TypeScript — ใครอ่านแค่สองบรรทัดแรกแล้วหันไปทำอย่างอื่น จะเข้าใจผิดว่าผ่าน
· อ่านจนบรรทัดสุดท้ายเสมอ หรือดู exit code
ผลรวมรอบที่ 1: ไม่ผ่าน → ย้อนกลับไป Code (ตามสไลด์หน้า 17) · กรณีนี้แผนไม่ได้ผิด แค่พิมพ์ผิด จึงไม่ต้องย้อนไปไกลถึง Plan
รอบที่ 2 — แก้แล้ว ตรวจใหม่ทั้งชุด
แก้ sucess → success แล้วรันทุกตัวใหม่ (ไม่ใช่รันแค่ตัวที่เคยพัง):
$ npx tsc --noEmit; echo "rc=$?"
rc=0
$ npx eslint .; echo "rc=$?"
rc=0
$ npx next build
▲ Next.js 16.3.5 (Turbopack)
✓ Running next.config.ts took 16ms
Creating an optimized production build ...
✓ Compiled successfully in 1043ms
Running TypeScript ...
Finished TypeScript in 1078ms ...
Collecting page data using 5 workers ...
Generating static pages using 5 workers (0/4) ...
Generating static pages using 5 workers (1/4)
Generating static pages using 5 workers (2/4)
Generating static pages using 5 workers (3/4)
✓ Generating static pages using 5 workers (4/4) in 377ms
Finalizing page optimization ...
Route (app)
┌ ○ /
└ ○ /_not-found
○ (Static) prerendered as static content
เครื่องมือผ่านหมด แต่สไลด์บอกให้ “execute the code” ด้วย — เปิด npx next dev -p 387
แล้วดู HTML ที่หน้าเว็บส่งออกมาจริง:
$ curl -s http://localhost:387/ | grep -o '<button[^>]*>บันทึกแล้ว</button>'
<button type="button" tabindex="0" data-slot="button" class="group/button inline-flex ... (ตัดคลาสพื้นฐานยาว ๆ ตรงกลางออก) ... bg-success text-success-foreground hover:bg-success/80 h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2">บันทึกแล้ว</button>
$ grep -rho '\.bg-success[^{]*{[^}]*}' .next/static
.bg-success{background-color:var(--success)}
บรรทัดแรกยืนยันว่าปุ่มได้ class bg-success จริง ·
บรรทัดที่สองยืนยันว่า Tailwind สร้าง CSS ของ class นี้ออกมาจริง และมันชี้ไปที่ token
--success ที่เราเพิ่ม — ถ้าลืมเพิ่ม token ใน globals.css
class จะอยู่บนปุ่มแต่ปุ่มไม่มีสี และ tsc / eslint / build ไม่มีตัวไหนฟ้องเลย
— ผมลองจริงแล้ว โดยคืน globals.css เป็นฉบับก่อนแก้ (ไม่มี token) แต่คง button.tsx ไว้:
$ npx tsc --noEmit; echo "tsc rc=$?"
tsc rc=0
$ npx eslint .; echo "eslint rc=$?"
eslint rc=0
$ npx next build > b.log 2>&1; echo "build rc=$?"
build rc=0
$ grep -rho '\.bg-success[^{]*{[^}]*}' .next/static; echo "grep rc=$?"
grep rc=1
ผ่านครบสามด่าน แต่ CSS ของ bg-success ไม่ถูกสร้างเลย (grep rc=1 = ไม่เจอ)
— นี่คือเหตุผลที่สไลด์ใส่ “execute the code” ไว้ในรายการ ไม่ใช่แค่ lint กับ type-check
ขั้นสุดท้ายของ Evaluate: ย้อนดูโจทย์ตั้งต้น (คำเตือนหน้า 17) —
โจทย์คือ “ปุ่มสีเขียวสำหรับสถานะสำเร็จ” · มี variant success ✓ ·
ใช้ token ตามแผน ✓ · ทำงานทั้งโหมดสว่างและมืด ✓ · ไม่แตะ variant เดิม ✓ · diff 9 บรรทัดใน 3 ไฟล์ตามแผน ✓
→ ผ่าน ไป Commit ได้
Evaluate เหมือนตรวจของก่อนส่งพัสดุ — ชั่งน้ำหนักผ่าน (build ผ่าน) ·
กล่องไม่บุบ (lint ผ่าน) · ที่อยู่เขียนถูกรูปแบบ (type ผ่าน)
แต่ถ้าไม่เปิดดูว่าของในกล่องคือของที่ลูกค้าสั่ง คุณอาจส่งเสื้อไซซ์ M ไปให้คนที่สั่งไซซ์ L
อย่างเรียบร้อยสวยงามผ่านทุกด่าน — นั่นคือคำเตือน “check the original requirements”
ที่ไม่เหมือนกัน: ด่านตรวจพัสดุแต่ละด่านดูของคนละอย่างชัดเจน คนตรวจรู้ตัวว่าตัวเองไม่ได้ดูอะไร
แต่เครื่องมือตรวจโค้ดไม่บอกว่ามันไม่ได้ดูอะไร — eslint ตอบ rc=0
ด้วยหน้าตาเดียวกันทั้งตอนที่โค้ดถูกและตอนที่ type ผิด (เห็นกับตาในรอบที่ 1)
· คุณต้องรู้เองว่าแต่ละตัวครอบคลุมอะไร ถึงจะรู้ว่าต้องรันครบชุด
เชื่อมกับตอนเขียน Go
คุณทำขั้น Evaluate มาตลอด 9 วันแล้ว แค่ชื่อเครื่องมือเปลี่ยน:
| ตรวจอะไร | ฝั่ง Go (วัน 1–9) | ฝั่ง Next.js (โปรเจกต์นี้) | ต่างกันตรงไหน |
|---|---|---|---|
| type ถูกไหม | go build |
npx tsc --noEmit |
Go คอมไพล์ไม่ผ่าน = ไม่มีไฟล์ให้รัน · TypeScript แยก “ตรวจ type” ออกจาก “รัน” ได้ ต้องสั่งตรวจเอง |
| แพตเทิร์นอันตราย / สไตล์ | go vet |
npx eslint . |
เหมือนกันตรงที่ไม่ได้ตรวจ type · eslint ของ Next มีกฎ React ด้วย เช่นลืม key |
| ประกอบทั้งโปรเจกต์ได้จริง | go build ./... |
npx next build |
next build รัน TypeScript ให้ในตัว และสร้างหน้า static ด้วย · ช้ากว่า tsc มาก |
| พฤติกรรมถูกไหม | go test ./... |
ยังไม่มี — package.json ไม่มีสคริปต์ test |
ในโปรเจกต์นี้ “รันจริงแล้วดูผล” (next dev + curl) คือสิ่งที่ใกล้ test ที่สุด |
| ดูว่าแก้อะไรไปบ้าง | git diff |
git diff |
เหมือนกันทุกตัวอักษร |
ขั้น 5 · Commit — บันทึกงาน หลังตรวจผ่านเท่านั้น
สไลด์หน้า 18 (ถอดตามจริง):
- “Write a clear commit message describing the change, and record the work.” — เขียนข้อความ commit ที่ชัด บอกว่าเปลี่ยนอะไร แล้วบันทึกงานไว้
“Only reached after Eval passes.” — มาถึงขั้นนี้ได้หลัง Eval ผ่านแล้วเท่านั้น
diagram หน้า 12: Review the changes (diff) · Write a meaningful commit message · Commit the changes · (Optional) Open a Pull Request · Summarize what was done · เป้าหมาย: A clean commit ready for review and merge.
$ git diff --stat
app/globals.css | 6 ++++++
app/page.tsx | 2 ++
components/ui/button.tsx | 1 +
3 files changed, 9 insertions(+)
$ git add app/globals.css app/page.tsx components/ui/button.tsx
$ git commit -m "feat(button): add success variant" -m "- add --success / --success-foreground tokens (light + dark) in globals.css
- add success variant to buttonVariants, same shape as default
- use <Button variant=\"success\"> on home page
checked: tsc --noEmit, eslint ., next build"
[main a09e653] feat(button): add success variant
3 files changed, 9 insertions(+)
$ git log --oneline -3
a09e653 feat(button): add success variant
2b21702 base
3f8a92e Initial commit from Create Next App
(เลข commit อย่าง a09e653 คำนวณจากเนื้อหา + ชื่อคน + เวลาที่ commit —
ทำแบบเดียวกันเป๊ะในเครื่องคุณจะได้เลขอื่น ไม่ใช่เรื่องผิด)
สามอย่างที่ทำให้ commit นี้ “clean” ตามสไลด์:
git diff --statก่อนadd— ตัวเลข 3 ไฟล์ 9 บรรทัดตรงกับแผน ถ้าเห็นไฟล์ที่ไม่อยู่ในแผนโผล่มา (เช่นpackage-lock.jsonเปลี่ยนทั้งที่ไม่ได้ลงแพ็กเกจ) ให้หยุดถามก่อนgit addระบุชื่อไฟล์ ไม่ใช่git add .— คุมได้ว่า commit มีแค่สิ่งที่ตั้งใจ- ข้อความสองชั้น: บรรทัดแรกสั้น บอกอะไร (อ่านใน
git log --onelineรู้เรื่อง) · ย่อหน้าล่างบอกรายละเอียดและตรวจด้วยอะไร — ตรงกับ “Summarize what was done” ในสไลด์
# ต่อจากเฉลยภารกิจท้ายบท (ที่หน้าแรกมี <Badge variant="success"> แล้ว)
# แยก branch ไปลองข้าม Evaluate — จงใจพิมพ์ badge variant ผิดเป็น "sucess"
# แล้ว commit ทันทีโดยไม่รัน tsc ก่อน
$ git commit -am "feat: use success badge"
git จะยอม commit ไหม?
ผลจริง:
[skip-eval abe7082] feat: use success badge
1 file changed, 1 insertion(+), 1 deletion(-)
$ npx tsc --noEmit; echo "rc=$?"
app/page.tsx(60,18): error TS2820: Type '"sucess"' is not assignable to type '"link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "success" | null | undefined'. Did you mean '"success"'?
rc=2
git รับไปเฉย ๆ ไม่มีคำเตือนสักคำ — git เก็บ “ไฟล์หน้าตาแบบนี้” ไม่ได้สนว่าโค้ดถูกหรือผิด
คำเตือน “Only reached after Eval passes” ในสไลด์จึงไม่มีเครื่องมือไหนบังคับให้
(ถ้าไม่ได้ตั้ง hook หรือ CI ไว้) — มันเป็นวินัยของคุณหรือของ agent ล้วน ๆ
และ commit พัง ๆ ที่หลุดเข้าไปแล้ว คนที่ git pull ต่อจากคุณจะได้โปรเจกต์ที่ build ไม่ผ่านทันที
- type-check / lint
- type-check ตรวจว่าค่าแต่ละตัวมีชนิดตรงกับที่ประกาศ (
tsc) · lint ตรวจแพตเทิร์นอันตรายและสไตล์ (eslint) · คนละเรื่องกัน ผ่านอันหนึ่งไม่ได้แปลว่าผ่านอีกอัน · พิมพ์บอก AI ได้ว่า “run both tsc --noEmit and eslint, lint passing is not enough” - exit code (
rc) - ตัวเลขที่คำสั่งคืนตอนจบ ·
0= สำเร็จ · ไม่ใช่ 0 = มีปัญหา ·tscกับeslintที่ผ่านจะไม่พิมพ์อะไรเลย จึงต้องecho "rc=$?"ดูเอง · agent ก็อ่านตัวเลขนี้เพื่อตัดสินใจว่าจะวนกลับไป Code หรือไปต่อ - regression
- ของที่เคยทำงานได้ แล้วพังเพราะงานใหม่ของเรา · diagram หน้า 12 เขียนว่า Ensure no regressions ·
ขั้น Explore ที่
grepหาคนใช้ Button อยู่ ก็คือการประเมินความเสี่ยงเรื่องนี้ล่วงหน้า · ถาม senior ได้ว่า “แก้ตรงนี้แล้วมีหน้าไหนเสี่ยง regression บ้าง” - scope creep
- งานที่ค่อย ๆ บวมเกินที่ตกลงกันไว้ทีละนิด — สิ่งที่สไลด์หน้า 16 เรียกว่า improvising a new scope mid-stream · บอก AI ได้ว่า “stick to the plan, list anything else you notice as follow-ups instead of doing it”
ข้ามขั้นนี้แล้วเกิดอะไร
ตารางสรุปจากตัวอย่างในบทนี้ — ทุกช่องมาจากสิ่งที่เห็นจริงข้างบน:
| ข้ามขั้น | อาการที่จะเจอ | เห็นในบทนี้ตรงไหน |
|---|---|---|
| 1 · Explore | ทำของที่มีอยู่แล้วซ้ำ · เขียนผิด convention ของโปรเจกต์ · แก้แล้วกระทบไฟล์ที่ไม่รู้ว่ามีคนใช้ | “Add dark mode” — โปรเจกต์มีชุดสีโหมดมืดอยู่แล้ว · ถ้าไม่เจอ --destructive จะเขียน bg-green-600 ตรง ๆ แล้วโหมดมืดไม่เปลี่ยนสี |
| 2 · Plan | ไม่มีใครจับทางผิดได้จนโค้ดเสร็จแล้ว · ไม่มีเกณฑ์ว่า “เสร็จ” คืออะไร ขั้น Evaluate จึงไม่รู้จะเทียบกับอะไร | แผนระบุ 3 ไฟล์ + วิธีตรวจ — ทำให้ตอน git diff --stat รู้ทันทีว่า 3 ไฟล์ 9 บรรทัด “ตรงแผน” |
| 3 · Code ตามแผน (ด้นสดแทน) |
diff บวม คนรีวิวหาบรรทัดสำคัญไม่เจอ · ถ้าพังก็ไม่รู้ว่าพังเพราะส่วนไหน | ข้อความ “To get started…” ใน page.tsx ที่ชวนลบ แต่ไม่อยู่ในแผน |
| 4 · Evaluate | commit ของพังเข้า history · คนถัดไป pull แล้ว build ไม่ผ่าน | branch skip-eval — git รับ "sucess" ไปเฉย ๆ แต่ tsc ได้ rc=2 |
| 4 · Evaluate (ตรวจไม่ครบชุด) |
เชื่อเครื่องมือตัวเดียวที่ไม่ได้ครอบเรื่องนั้น | eslint ได้ rc=0 ทั้งที่ type ผิด · next build ขึ้น ✓ Compiled successfully ก่อนพัง |
| 5 · Commit | งานดีแต่ไม่มีบันทึก · ย้อนกลับไม่ได้ · คนอื่นไม่รู้ว่าเปลี่ยนอะไรและตรวจด้วยอะไร | ย่อหน้า checked: tsc --noEmit, eslint ., next build ในข้อความ commit |
ภารกิจ — เดินครบ 5 ขั้นด้วยตัวเอง
🎯 เพิ่ม variant success ให้ Badge
ต่อจาก commit feat(button): add success variant ข้างบน ในโปรเจกต์ของคุณเอง (บท 79–85) ·
ทำทีละขั้น และจดสิ่งที่ได้ของแต่ละขั้นลงกระดาษก่อนไปขั้นถัดไป:
- Explore — Badge อยู่ไฟล์ไหน · variant ที่คล้ายกันที่สุดเขียนยังไง · token
--successมีอยู่แล้วหรือยัง · มีใครใช้ Badge อยู่ไหม - Plan — เขียนแผนไม่เกิน 10 บรรทัด: ไฟล์ที่เปลี่ยน · หน้าตาแบบไหน (พื้นทึบหรือพื้นจาง) · ตรวจยังไงว่าเสร็จ
- Code — ทำตามแผน ห้ามแตะอะไรนอกแผน
- Evaluate — รัน
tsc --noEmit·eslint .·next buildครบทั้งสาม แล้วเทียบกับแผน - Commit —
git diff --statก่อน แล้ว commit พร้อมข้อความที่บอกว่าตรวจด้วยอะไร
ถ้าใช้ Claude Code ทำ ให้สั่งทีละขั้นแล้วอ่านสิ่งที่มันทำก่อนอนุญาตขั้นถัดไป — เป้าหมายคือคุณต้องบอกได้ทุกจุดว่ามันอยู่ขั้นไหน
Explore — ผลจริงบนโปรเจกต์เดียวกัน (หลัง commit ของ Button):
$ grep -rn "components/ui/badge" app components; echo "rc=$?"
rc=1
ยังไม่มีใครใช้ Badge → ไม่เสี่ยง regression · เปิด components/ui/badge.tsx จะเห็นว่าโครงเหมือน Button
(cva + รายการ variants) และ variant destructive ของ Badge เป็นแบบพื้นจาง
(bg-destructive/10 text-destructive) ไม่ใช่พื้นทึบ · token --success มีแล้วจาก commit ก่อน
Plan — 2 ไฟล์: เพิ่ม success ใน badge.tsx แบบพื้นจางตามแพตเทิร์น destructive ของ Badge ·
ใช้ <Badge variant="success"> ใน page.tsx หนึ่งจุด · ไม่เพิ่ม token ใหม่ (ใช้ของเดิม)
Code — บรรทัดที่เพิ่มใน badge.tsx:
success:
"bg-success/10 text-success dark:bg-success/20 [a]:hover:bg-success/20",
Evaluate + Commit — ผลจริง:
$ git grep -n "success" -- app components
app/globals.css:30: --color-success: var(--success);
app/globals.css:31: --color-success-foreground: var(--success-foreground);
app/globals.css:69: --success: oklch(0.527 0.154 150.069);
app/globals.css:70: --success-foreground: oklch(0.985 0 0);
app/globals.css:106: --success: oklch(0.723 0.219 149.579);
app/globals.css:107: --success-foreground: oklch(0.205 0 0);
app/page.tsx:59: <Button variant="success">บันทึกแล้ว</Button>
app/page.tsx:60: <Badge variant="success">ชำระแล้ว</Badge>
components/ui/badge.tsx:16: success:
components/ui/badge.tsx:17: "bg-success/10 text-success dark:bg-success/20 [a]:hover:bg-success/20",
components/ui/button.tsx:19: success: "bg-success text-success-foreground hover:bg-success/80",
$ npx tsc --noEmit; echo "rc=$?"
rc=0
$ npx eslint .; echo "rc=$?"
rc=0
$ npx next build
... (ส่วนต้นเหมือนรอบก่อน)
Route (app)
┌ ○ /
└ ○ /_not-found
○ (Static) prerendered as static content
$ git diff --stat
app/page.tsx | 2 ++
components/ui/badge.tsx | 2 ++
2 files changed, 4 insertions(+)
$ git commit -am "feat(badge): add success variant using --success token"
[main fc006e0] feat(badge): add success variant using --success token
2 files changed, 4 insertions(+)
จุดตรวจตัวเอง: ถ้า diff ของคุณมี globals.css ด้วย แปลว่าขั้น Explore
ไม่เจอว่า token มีอยู่แล้ว แล้วเพิ่มซ้ำ · ถ้าคุณเลือกพื้นทึบแบบ Button ก็ไม่ผิด แต่
ต้องเป็นการตัดสินใจที่เขียนไว้ในแผน ไม่ใช่เพราะก็อปบรรทัดจาก Button มาเฉย ๆ
(git commit -am ใช้ได้ในกรณีนี้เพราะ diff --stat เพิ่งยืนยันว่ามีแค่ 2 ไฟล์ที่ตั้งใจ ·
ถ้ายังไม่ได้ดู ใช้ git add ระบุชื่อไฟล์ปลอดภัยกว่า)
ลองตอบดู
โจทย์คือ “ปุ่มสีเขียวบนหน้ายืนยันการชำระเงิน” · agent เพิ่ม variant แล้ววางปุ่มไว้หน้าแรก · tsc · eslint · next build ผ่านหมด — ความผิดพลาดนี้ควรถูกจับได้ที่ไหน?
ตัวเลือกที่ 3 ถูก · สไลด์หน้า 17 เขียนคำเตือนไว้ตรง ๆ ว่า “Should check the original requirements.”
และ diagram หน้า 12 ใส่ Validate requirements ไว้ในขั้น Eval
ตัวเลือกที่ 4 ผิด เพราะเครื่องมือตรวจแค่ว่าโค้ดถูกหลักภาษา ไม่รู้ว่าโจทย์ขออะไร — ในบทนี้เราเห็นแล้วว่า
class บนปุ่มถูกแต่ถ้าลืม token ปุ่มก็ไม่มีสี และไม่มีเครื่องมือไหนฟ้อง ·
ตัวเลือกที่ 1 ผิดเพราะ Explore อ่านไฟล์ที่เกี่ยว ไม่ใช่ทุกไฟล์ และมันเกิดก่อนมีโค้ด จึงจับความผิดของโค้ดไม่ได้ ·
ตัวเลือกที่ 2 ผิดเพราะสไลด์หน้า 18 บอกว่า Commit “Only reached after Eval passes” — ถึงตอนนั้นควรตรวจเสร็จแล้ว
จุดที่ต้องจำ: เครื่องมือผ่าน = โค้ดถูกหลัก · ตรงโจทย์หรือเปล่า ต้องเปิดโจทย์เทียบเอง
ในรอบที่ 1 ที่จงใจพิมพ์ variant="sucess" ผิด — คำสั่งไหนไม่ฟ้องอะไรเลย?
ตัวเลือกที่ 2 ถูก · ผลจริงคือ npx eslint . ได้ rc=0 ไม่พิมพ์อะไรเลย
ตัวเลือกที่ 1 ผิด — tsc จับได้พร้อมบอก Did you mean '"success"'? เพราะ
cva ทำให้ variant มี type เป็นรายการสตริงที่แน่นอน ไม่ใช่ string อะไรก็ได้ ·
ตัวเลือกที่ 3 ผิด — next build ขึ้น ✓ Compiled successfully จริง แต่ยังไม่จบ
มันไปพังต่อที่ Running TypeScript แล้วจบด้วย Failed to type check. ·
ตัวเลือกที่ 4 ผิดเพราะ eslint ไม่ฟ้อง
จุดที่ต้องจำ: lint กับ type-check คนละเรื่อง — เหมือน go vet กับ go build · รันครบชุดทุกครั้ง
Evaluate ไม่ผ่านเพราะ tsc ฟ้องว่าพิมพ์ชื่อ variant ผิด · แผนเดิมยังถูกต้องทุกข้อ — ขั้นต่อไปที่ถูกคืออะไร?
ตัวเลือกที่ 1 ถูก · สไลด์หน้า 17: “If FAILURES here should be loop back to Code” และแผนยังถูก จึงไม่ต้องย้อนไปถึง Plan
(diagram หน้า 12 ให้ย้อนไป “the appropriate step” ซึ่งกรณีนี้คือ Code)
ตัวเลือกที่ 2 ผิดเพราะย้อนไกลเกินเหตุ ปัญหาไม่ได้อยู่ที่ความเข้าใจโปรเจกต์ ·
ตัวเลือกที่ 3 ผิดตรง ๆ กับคำเตือนหน้า 18 “Only reached after Eval passes” — และเราเห็นแล้วว่า git ไม่ห้ามให้ ·
ตัวเลือกที่ 4 ฟังดูประหยัดเวลา แต่การแก้รอบใหม่อาจทำอย่างอื่นพังได้ (เช่นแก้แล้วเผลอลบ import) ต้องตรวจครบชุดใหม่
จุดที่ต้องจำ: ไม่ผ่าน → กลับไปขั้นที่ต้นเหตุอยู่ → แก้ → ตรวจใหม่ทั้งชุด
ระหว่างเพิ่ม variant success ตามแผน agent เห็นว่าหน้าแรกยังเป็นหน้าตัวอย่างของ create-next-app เลยเขียนหน้าแรกใหม่ทั้งหน้าให้สวยขึ้นด้วย — ผิดหลักข้อไหน?
ตัวเลือกที่ 4 ถูก · สไลด์หน้า 16: “Implement what was planned rather than improvising a new scope mid-stream.”
ตัวเลือกที่ 2 ผิดเพราะ “build ผ่าน” ไม่ได้แปลว่าควรทำ — diff จาก 9 บรรทัดจะบวมเป็นหลายสิบ คนรีวิวไม่ได้ขอ และหาบรรทัดสำคัญไม่เจอ ·
ตัวเลือกที่ 3 แยก commit ได้จริง แต่แก้ปลายเหตุ งานนั้นไม่ควรเกิดขึ้นในรอบนี้ตั้งแต่แรก เพราะไม่ผ่านการวางแผนและรีวิว ·
ตัวเลือกที่ 1 ผิดเพราะปัญหาไม่ใช่อ่านไม่พอ แต่คือทำเกินที่ตกลง
จุดที่ต้องจำ: เจอของที่อยากแก้ระหว่างทาง → จดเป็นงานถัดไป · ไม่ยัดเข้างานปัจจุบัน
ถามกลับได้เลย
ถ้าคุณลองให้ Claude Code ทำงานแล้วบอกไม่ได้ว่ามันอยู่ขั้นไหน — เอาสิ่งที่มันพิมพ์ออกมาทั้งก้อนมาคุยได้
เราจะไล่ด้วยกันว่าตรงไหนคือ Explore ตรงไหนมันข้าม Plan ไป และมันรันตรวจครบชุดจริงหรือแค่บอกว่าตรวจแล้ว
หรือถ้า tsc ฟ้อง error ที่ไม่ได้มาจากโค้ดที่คุณแก้ (แบบ LayoutProps ในบทนี้) ก็เอาข้อความมาได้เลย
อ้างอิง
- Claude Code — Best practices — อ่านหัวข้อ Explore first, then plan, then code (4 ขั้น Explore · Plan · Implement · Commit พร้อม prompt ตัวอย่างของแต่ละขั้น และกล่องที่บอกว่าเมื่อไหร่ควรข้ามการวางแผน) · แล้วอ่าน Give Claude a way to verify its work ซึ่งอธิบายว่าทำไม agent ต้องมีคำสั่งตรวจที่ให้ผลผ่าน/ไม่ผ่าน — ตรงกับขั้น Evaluate ของสไลด์ (สังเกตว่าเอกสารทางการรวมการตรวจไว้ในขั้น Implement ส่วนสไลด์แยกออกมาเป็นขั้นของตัวเอง)
- Claude Code — Common workflows — อ่านหัวข้อ Plan before editing สำหรับวิธีเปิด plan mode และ Understand new codebases สำหรับตัวอย่างคำถามที่ใช้ในขั้น Explore
- Anthropic — Building effective agents — อ่านหัวข้อ What are agents? เทียบกับนิยามในสไลด์หน้า 12 และ Workflow: Evaluator-optimizer ซึ่งคือลูป “ทำ → ประเมิน → แก้” แบบเดียวกับเส้น Evaluate→Code
- Next.js —
nextCLI — อ่านส่วนของคำสั่งnext typegenที่ใช้แก้ errorLayoutPropsในบทนี้ และnext buildที่รัน type-check ให้ในตัว