Uncle Bob ตั้งเป้าเลิกอ่านโค้ดที่ agent เขียน แล้วใช้ CRAP score กับ mutation testing ตัดสินแทน
Robert C. Martin เลิกยัดกฎลงในพรอมป์ แล้วให้ CRAP score กับ mutation testing ตัดสินโค้ดที่ agent เขียนแทน นี่คือวิธีทำงานจริงของเขา

Robert C. Martin หรือ Uncle Bob ผู้เขียนหนังสือชื่อดังอย่าง Clean Code ตั้งเป้าว่าจะไม่อ่านโค้ดที่ coding agent เขียนให้อีกเลยแม้แต่บรรทัดเดียว
เขาไม่ได้ปล่อยผ่านเรื่องคุณภาพ แต่เปลี่ยนจากการตรวจด้วยสายตาตัวเอง ไปใช้เครื่องมือที่ให้คะแนนโค้ดได้ตรงๆ
เขาพูดเรื่องนี้ในไลฟ์ยาวเกือบหนึ่งชั่วโมงบนช่อง YouTube ของ Matt Pocock เมื่อวันที่ 19 สิงหาคม ซึ่งมีคนดูสดพร้อมกัน 1,500 คน โดยเขาเป็นโปรแกรมเมอร์ที่เขียนโค้ดมานานกว่า 50 ปี เริ่มตั้งแต่ตอนอายุ 12 ในปี 1964
เขาเพิ่งเริ่มใช้ agent อย่างจริงจังตอนคริสต์มาสปีที่แล้ว ตัวแรกที่หยิบมาใช้คือ Grok โมเดล AI รุ่นแรกๆ สิ่งที่เจอคือ agent ปิดงานได้เร็วจริง แต่ทิ้งความรกไว้ให้ตามเก็บกวาดจนตัวเองทำงานช้าลง โดยเขาเปรียบเทียบกองโค้ดที่เหลือทิ้งไว้ว่าเหมือน "กองขี้หมา"
คำถามสำคัญจึงไม่ใช่ว่า agent เขียนโค้ดแทนเราได้หรือยัง แต่คือใครจะบอกได้ว่าโค้ดกองนั้นใช้ได้จริง และคำตอบของเขาก็ไม่ใช่การอ่านให้ละเอียดขึ้น
กฎที่พิมพ์ไว้ใน CLAUDE.md มันแค่แนวทาง

วิธีคุม agent ที่คุ้นเคยกันดีคือเขียนกฎยาวๆ ลงในไฟล์กำหนดคำสั่งตั้งต้นอย่าง CLAUDE.md หรือ agents.md แล้วหวังว่ามันจะทำตาม แต่ Uncle Bob เลิกเชื่อวิธีนี้ เพราะโมเดลมองกฎพวกนั้นเป็นแค่คำแนะนำ ไม่ใช่กฎตายตัว เขาล้อว่าเหมือนประโยคในภาพยนตร์ Pirates of the Caribbean ที่บอกว่ากฎเป็นเพียงแนวทางคร่าวๆ ไม่ใช่ข้อบังคับ
เหตุผลทางเทคนิคมาจากปรากฏการณ์ lost in the middle ที่โมเดล AI มักลืมข้อความตรงกลาง โดยข้อความช่วงต้นและท้ายของ context window หรือพื้นที่ความจำที่โมเดลมองเห็นได้ในครั้งเดียว จะมีน้ำหนักต่อคำตอบมากกว่า กฎที่เราอุตส่าห์เขียนไว้ยาวสามหน้าจึงจมหายไปกลางกองพอดี
ในไลฟ์มีการเรียกช่วงต้นของ context window ว่า smart zone และช่วงที่ถัดออกไปว่า dumb zone ตามชื่อที่ Dex Hardy ตั้งไว้ โดยตัวเลขที่ยกมาคือช่วง 150,000 โทเคนแรก (หน่วยนับความยาวข้อความของโมเดล) ที่โมเดลยังประมวลผลได้แม่นยำ ยิ่งเลยจากจุดนั้นไป ความสนใจของโมเดลก็ยิ่งเจือจางลง
ตรงนี้พลาดได้ง่าย เพราะกฎที่โมเดลลืมจะไม่ส่งสัญญาณเตือนอะไรเลย งานยังเสร็จ เทสต์ยังเขียว แล้วเราค่อยไปเจอตอนรีวิวโค้ดว่ามีอีกสิบไฟล์ที่ไม่ได้ทำตามข้อตกลงที่เขียนไว้ตั้งแต่บรรทัดแรกของโปรเจกต์
คำแนะนำของเขาจึงสวนทางกับสัญชาตญาณ คือตัดพรอมป์เริ่มต้นให้สั้นที่สุดเท่าที่จะทำได้ แล้วทุ่มแรงไปกับการตรวจซ้ำทีหลังด้วยเครื่องมือแบบ deterministic ที่ให้ผลลัพธ์แน่นอนตายตัวทุกครั้งที่รัน โดยไม่ขึ้นกับอารมณ์ของโมเดล
CRAP score กับ mutation testing คือกฎที่เถียงไม่ได้
เครื่องมือที่เขาหยิบกลับมาใช้ไม่ใช่ของใหม่ แต่เป็นเทคนิคจากช่วงต้นทศวรรษ 2000 ที่ตอนนั้นเขามองว่าไม่คุ้มกับแรงคน แต่ตอนนี้กลับคุ้มค่าแล้ว เพราะงานน่าเบื่อซ้ำๆ แบบนี้คือสิ่งที่ agent ทำได้เร็วโดยไม่บ่นสักคำ
ตัวแรกคือระบบวัดความเสี่ยงของโค้ดอย่าง CRAP score โดยสูตรจะนำค่า test coverage หรือสัดส่วนโค้ดที่ผ่านการทดสอบจริง มาคำนวณร่วมกับ cyclomatic complexity ซึ่งเป็นค่าวัดความซับซ้อนจากจำนวนทางแยกเงื่อนไขในฟังก์ชัน แล้วประเมินออกมาเป็นคะแนนรายฟังก์ชัน ฟังก์ชันที่มีทางแยกเยอะแต่เทสต์แทบไม่แตะจะได้คะแนนสูง และคะแนนยิ่งสูงก็ยิ่งแย่
ตัวที่สองคือวิธีทดสอบด้วยการแกล้งแก้โค้ดอย่าง mutation testing ลองนึกภาพโปรแกรมที่เข้าไปไล่สลับเครื่องหมายในซอร์สโค้ดของเรา เช่น เปลี่ยนบวกเป็นลบ เปลี่ยนน้อยกว่าเป็นมากกว่า หรือเปลี่ยนเท่ากับเป็นไม่เท่ากับ แล้วรันเทสต์ทั้งชุดใหม่ทุกครั้ง
สิ่งที่ควรเกิดขึ้นคือเทสต์ต้องพัง เพราะโค้ดถูกแก้ให้ผิดไปแล้ว ถ้าเทสต์ยังเขียวอยู่ แปลว่าชุดทดสอบไม่เคยตรวจบรรทัดนั้นจริง โค้ดผิดที่หลุดรอดมาได้มีชื่อเรียกว่า surviving mutant หรือตัวกลายพันธุ์ที่รอดชีวิต และหน้าที่ของคนเขียนคือต้องไล่กำจัดมันให้หมด
จุดที่ชวนสับสนคือ mutation testing ไม่ได้บอกว่าโค้ดของเราถูกต้อง แต่บอกว่าเทสต์ของเราจับผิดเป็นหรือไม่ ซึ่งเป็นคนละประเด็นกัน และเป็นเรื่องสำคัญมากเมื่อคนเขียนเทสต์ไม่ใช่คนอีกต่อไป เรื่องนี้ตรงกับการทดลองที่เคยเล่าไว้ว่า งานที่ agent ทำผ่านเทสต์บางส่วนเป็นการผ่านแบบฟลุก การที่เทสต์ผ่านจึงไม่ใช่หลักฐานยืนยันว่าวิธีที่ใช้ถูกต้อง
พอมีเครื่องมือสองอย่างนี้ สิ่งที่เขาทำคือสั่งให้ agent รันทั้ง CRAP และ mutation testing ด้วยตัวเองหลังเขียนโค้ดเสร็จ แล้วแก้ไปเรื่อยๆ จนผ่านเกณฑ์ ตัวเขาเองไม่ต้องอ่านโค้ดทีละบรรทัด เหลือแค่ดูคะแนนกับสุ่มเปิดดูเป็นจุดๆ
ไปป์ไลน์ agent 5 ตัว แทน agent ตัวเดียวที่ทำทุกอย่าง
ถัดจากเครื่องมือคือวิธีจัดคน หรือในที่นี้คือวิธีจัด agent เขาไม่ยัดทุกงานให้ตัวเดียว แต่ร้อยเรียงเป็นไปป์ไลน์ 5 ขั้นตอน โดยแต่ละขั้นจะรับงานต่อจากขั้นก่อนหน้า
- specifier ทำหน้าที่แปลงเอกสารความต้องการของคนให้อยู่ในโครงสร้าง Gherkin ซึ่งเป็นรูปแบบการเขียนเงื่อนไขทดสอบด้วยภาษาที่คนทั่วไปอ่านเข้าใจ พร้อมขั้นตอน QA ที่ตรวจคุณภาพจากมุมของผู้ใช้จริงผ่านหน้าจอ
- coder ทำหน้าที่เขียน unit test ซึ่งเป็นชุดทดสอบการทำงานย่อย และเขียนโค้ดตาม story หรือโจทย์ความต้องการของผู้ใช้ เพื่อให้เงื่อนไขใน Gherkin ผ่านครบ
- cleaner สำหรับรัน CRAP analysis และรีวิวโค้ดทั่วไป เก็บกวาดของที่ coder ทิ้งไว้
- hardener สำหรับรัน mutation testing แบบไม่ปรานี เป้าคือ coverage เต็มร้อยและไม่เหลือ mutant รอดสักตัว
- QA agent สำหรับแปลงเอกสาร QA เป็นสคริปต์ที่รันได้จริง แล้วตรวจผลลัพธ์แบบ deterministic
ข้อดีที่ได้ไม่ใช่แค่ความเป็นระเบียบ แต่ละตัวยังรันขนานกันได้ และที่สำคัญกว่าคือ context window ของแต่ละตัวแคบลงมาก ปัญหา lost in the middle จึงลดลงตามไปด้วย agent ที่โฟกัสงานเดียวจึงทำตามกฎที่กำหนดไว้ได้ดีกว่าตัวที่ต้องแบกทุกเรื่องพร้อมกัน แนวคิดนี้ใกล้เคียงกับ ระบบที่ส่ง AI หลายตัวมารีวิวโค้ดคนละมุม ซึ่งมีการแบ่งหน้าที่อย่างชัดเจนในลักษณะเดียวกัน
แต่วิธีนี้ก็มีสิ่งที่ต้องแลก เพราะแต่ละ agent ใช้เวลาเริ่มต้นราว 10-15 วินาที และการส่งต่องานระหว่างกันก็มีต้นทุนของมัน ตัวเลขที่เขายกมาคืองานชิ้นหนึ่งที่ agent ตัวเดียวทำเสร็จใน 5 นาที ถ้าส่งเข้าทั้งไปป์ไลน์จะใช้เวลาราวหนึ่งชั่วโมง
สำหรับเขา เมื่อเทียบกับการลงมือทำเองครึ่งวัน หนึ่งชั่วโมงก็ยังเร็วขึ้นราว 4-5 เท่า แถมยังได้คุณภาพดีกว่าตอนทำเองด้วย
เกณฑ์ของ agent ไม่ต้องเท่ากับของคน
ส่วนที่หยิบไปปรับใช้กับทีมได้ทันทีคือเรื่องตัวเลข เพราะเขาลงรายละเอียดไว้ว่าควรปรับเกณฑ์อย่างไรเมื่อเปลี่ยนจากการตรวจด้วยคนมาเป็นเครื่องมือ
CRAP score ที่เขาตั้งไว้สำหรับงานของมนุษย์คือต่ำกว่า 4 แต่พอเป็นงานของ agent เขาผ่อนเกณฑ์ขึ้นมาเป็น 6 และกำลังคิดว่าจะขยับไปถึง 8 ดีหรือไม่ เพราะยังหาจุดลงตัวไม่เจอ ตัวเลขนี้จึงไม่ใช่ค่ามาตรฐานที่หยิบไปใช้ตามได้ทันที แต่เป็นตัวอย่างของการยอมรับว่าเกณฑ์ของคนกับ agent ไม่จำเป็นต้องเท่ากัน
เรื่องวินัยก็เช่นเดียวกัน แนวทางการเขียนเทสต์นำโค้ดอย่าง TDD แบบเคร่งครัด ที่ต้องสลับเขียนเทสต์กับโค้ดทีละนิดจนเป็นจังหวะ เป็นวินัยที่ออกแบบมาเพื่อรองรับข้อจำกัดด้านความจำระยะสั้นของมนุษย์ เขาจึงไม่บังคับ agent แบบนั้น แต่ปล่อยให้เขียนทั้งฟังก์ชันจบก่อนแล้วค่อยเขียนเทสต์ตาม เพราะความจำระยะสั้นของโมเดลใหญ่และแม่นยำกว่าเรามาก ซึ่งสอดคล้องกับ การทดลองสั่ง agent ให้ทำ TDD ในลูปตัวเอง ที่วัดแล้วไม่พบความต่างของคุณภาพชัดเจน แต่ต้องจ่ายโทเคนเพิ่มขึ้นหลายเท่า
ข้อสรุปของเขากินความไกลกว่าเรื่องตัวเลข การนำ "พฤติกรรม" ของมนุษย์ไปบังคับ agent อาจเป็นความผิดพลาด แต่การนำ "คุณค่า" ของมนุษย์ไปกำกับดูแลนั้นไม่ผิด เพียงแต่ต้องยอมปรับเส้นที่ใช้วัด
แผนใหญ่ล่วงหน้าพัง เลยกลับไปทำทีละ story
ก่อนจะมาถึงวิธีนี้ เขาลองวางแผนละเอียดล่วงหน้าทั้งก้อนแล้วส่งให้ agent ทำตาม ผลคือล้มเหลวซ้ำแล้วซ้ำเล่า เพราะแผนไม่เคยตรงกับสิ่งที่เกิดขึ้นจริง จนต้องหยุดกลางทาง กลับไปแก้แผน แล้วเริ่มใหม่อยู่ตลอด
เขาจึงหันกลับไปใช้วิธีการทำงานแบบ agile ที่เน้นหยิบงานทีละ story มาทำ แล้วค่อยถอยมาดูสถาปัตยกรรมเป็นระยะ เจอจุดที่เพี้ยนก็ปรับแก้ด้วยตัวเอง จากการทดลองของเขา แนวทางการพัฒนาโดยอิงพิมพ์เขียวละเอียดอย่าง spec-driven development ที่เขียนสเปกใหญ่ให้ครบก่อนลงมือยังไม่ให้ผลดีนัก เพราะ agent มักเขียนแผนที่ดูสวยหรู แต่พอทำจริงมักไปพังเอาตอนท้าย
เขาเปรียบเทียบให้เห็นภาพชัดเจนว่า ถ้าการซ่อมบ้านแต่ละครั้งมีต้นทุนแค่หนึ่งดอลลาร์ รวมทั้งงานฐานรากและหลังคา เราคงไม่จ้างสถาปนิกมานั่งวางแผนให้สมบูรณ์แบบก่อน สู้ค่อยๆ ปรับหน้างานไปเลยดีกว่า ซึ่งต้นทุนการแก้โค้ดในยุคที่มี agent ก็ลดลงจนเข้าใกล้ศูนย์แล้ว
ผลพลอยได้ที่ตามมาคือเขาไม่เก็บสเปกไว้ถาวรในโปรเจกต์ แต่ใช้เสร็จแล้วทิ้ง เพราะมองว่าผลลัพธ์สุดท้ายที่รันได้นั่นแหละคือสเปกตัวจริง ไม่ใช่เอกสารที่เขียนไว้ตั้งแต่ยังไม่รู้อะไรเลย
โครงสร้างโมดูลคือส่วนที่ยังต้องตัดสินเอง
จนถึงเมื่อราวหนึ่งเดือนก่อน งานแบ่งโมดูลยังเป็นสิ่งที่เขาต้องลงมือทำเองทั้งหมด วิธีทำคือถาม agent ว่าโมดูลไหนเชื่อมกับอะไรบ้าง แล้วเขียนแผนการแบ่งโมดูลส่งกลับไปให้ทำตาม
หลังจากนั้นเขาให้ agent ช่วยสร้าง architecture viewer ซึ่งเป็นเครื่องมือแสดงภาพรวมระบบ โดยแสดงผังโครงสร้างมาตรฐานอย่าง UML diagram ของโมดูลและ dependency ทั้งระบบ พร้อมทั้งคลิกลงไปดูโมดูลย่อยกับโค้ดจริงข้างในได้ด้วย
นอกจากนี้ยังมีเครื่องมืออีกตัวที่กำหนดไว้อย่างชัดเจนว่าโมดูลไหนพึ่งพาโมดูลไหนได้บ้าง ถ้า agent ละเมิดกฎข้อไหนก็ต้องแก้ไข ไม่ว่าจะเป็นการกลับทิศทางของ dependency เพิ่มจุดเชื่อมต่อคั่นกลางอย่าง interface หรือแยกโมดูลออกจากกัน
เหตุผลที่โครงสร้างสำคัญกับโมเดลมากกว่าที่คิด มาจากแนวคิด deep modules ของ John Ousterhout อาจารย์ด้านวิทยาการคอมพิวเตอร์ที่เขาอ้างถึง โดยโมดูลตื้นคือโมดูลที่มี interface กว้างมากแต่ข้างในบางเฉียบ ส่วนโมดูลลึกคือโมดูลที่มี interface แคบๆ แต่ซ่อนรายละเอียดไว้ข้างในเยอะ ซึ่งแบบหลังทำงานกับโมเดลได้ดีกว่า เพราะโมเดลอ่านแค่ interface ก็ใช้งานได้ทันที ไม่ต้องแบกรายละเอียดข้างในเข้าไปใน context เลย ใครอยากศึกษาเรื่องนี้ต่อ หนังสือ A Philosophy of Software Design คือต้นทางของแนวคิดนี้ และ Clean Code ฉบับพิมพ์ครั้งที่สองก็มีภาคผนวกที่เป็นบทสนทนาถกเถียงระหว่างทั้งสองคนอยู่ด้วย
งานแบ่งโมดูลยังต้องอยู่ในมือคน เพราะเป็นสิ่งที่ Ousterhout เรียกว่า strategic programming หรือการตัดสินทิศทางระดับภาพรวม ส่วนงานลงมือทำทีละจุดอย่าง tactical programming นั้น agent เก่งมากอยู่แล้ว แต่งานระดับทิศทางคือจุดที่มันยังทำได้ไม่ดี
เพิ่งเริ่มเขียนโค้ดปีนี้ ควรเริ่มจากตรงไหน
คำแนะนำของเขาสำหรับคนที่เพิ่งเริ่มต้น ค่อนข้างสวนทางกับกระแสในตอนนี้ ข้อแรกคือควรเขียนโค้ดด้วยตัวเองอย่างน้อยราวหนึ่งปีก่อน เพื่อให้เข้าใจว่าสิ่งที่ agent กำลังจัดการอยู่นั้นคืออะไร
ข้อสองแรงกว่านั้น เมื่อเข้าไปทำงานในบริษัทที่ใช้ agent อย่างหนัก เขามองว่าควรปฏิบัติต่อคนใหม่เหมือนเป็น agent ตัวหนึ่ง คือรับงานแบบเดียวกับที่ agent ได้รับ และผ่านการตรวจสอบด้วยเครื่องมือ deterministic ชุดเดียวกัน โดยควรใช้เวลาหลายเดือนในการเรียนรู้อย่างเต็มที่ แม้จะยังสร้างผลงานได้ไม่มากนัก ก่อนจะได้รับความไว้วางใจให้คุม agent ของตัวเอง
เส้นทางที่เขาวางไว้คือเริ่มจากภาษาเลขฐานสองอย่าง binary ต่อด้วย assembly ซึ่งเป็นภาษาเครื่องระดับต่ำ จากนั้นจึงขยับไปภาษา C แล้วขยับไปภาษาระดับสูงอย่าง Python ก่อนจะเริ่มทำงานร่วมกับ agent และเครื่องมือตรวจ จนสุดท้ายถึงจะคุม agent ในระดับทิศทางได้ ระหว่างทางเขาแนะนำให้กลับไปอ่านหนังสือคลาสสิกที่คนยุคนี้แทบไม่แตะ ทั้งงานเขียนของผู้บุกเบิกวงการอย่าง Tom DeMarco, Ed Yourdon และหนังสืออย่าง The Pragmatic Programmer ซึ่งแม้เนื้อหาบางส่วนจะล้าสมัยไปตามยุคสมัย แต่สิ่งที่ยังได้คือวิธีคิดในการมองงานเป็นภาพรวม
สัญญาณที่เขาใช้สังเกตว่า agent กำลังหลงทาง ก็มาจากประสบการณ์เดิมเช่นกัน เขาไม่ได้ดูแค่ว่าโค้ดรก แต่ดูอาการดิ้น คือเวลาที่ agent แก้จุดหนึ่งแล้วไปพังอีกจุด จนวนกลับมาแก้จุดเดิมซ้ำๆ ซึ่งอาการนี้เขาจำได้ขึ้นใจ เพราะเคยเป็นคนที่ดิ้นแบบนั้นมาก่อนด้วยตัวเอง
เมื่อถูกถามปิดท้ายว่าทำไมพื้นฐานยังสำคัญ เขายกคำกล่าวที่จำไม่ได้แน่ชัดว่าเป็นของใคร แต่น่าจะเป็น Dijkstra นักวิทยาศาสตร์คอมพิวเตอร์ชื่อดัง ที่บอกว่าซอฟต์แวร์คือสิ่งที่ซับซ้อนที่สุดเท่าที่มนุษย์เคยพยายามสร้าง พื้นฐานทั้งหมดจึงเป็นวิธีจัดระเบียบความซับซ้อนนั้นให้สมองมนุษย์รับไหว และตอนนี้รวมถึงโมเดลที่สร้างขึ้นมาตามแบบมนุษย์ด้วย ทุกครั้งที่วงการขยับสู่ระดับใหม่ จาก binary ไป assembly ไปสู่คอมไพเลอร์ จนมาถึง agent ในปัจจุบัน คนที่คุ้นเคยกับระดับเดิมก็มักบ่นเหมือนกันหมดว่าของจะพังและใครๆ ก็เขียนโค้ดได้ แต่สุดท้ายกฎพื้นฐานเดิมก็ยังคงใช้ได้ผลเสมอ
กฎที่เราโยนทิ้งในวันนี้ คือกฎที่ในอีกหนึ่งปีข้างหน้า เราจะก้มลงเก็บขึ้นมาปัดฝุ่น แล้วเข้าใจได้เองว่าทำไมถึงต้องมีมัน
เครื่องมือที่เขาเขียนไว้ ทั้งตัววัดค่า CRAP สำหรับภาษาอย่าง Clojure, Java และ Go ตลอดจนตัวรัน mutation testing และตัวคุม agent เขาไม่แนะนำให้ดาวน์โหลดไปใช้ตรงๆ ด้วยซ้ำ แต่วิธีที่เขาแนะนำคือให้ agent ของเราไปดูตัวอย่างพวกนั้น แล้วสร้างเวอร์ชันที่เข้ากับงานของเราขึ้นมาเอง
ความรู้พื้นฐานไม่ได้หายไปจากงานของเรา แต่แค่ย้ายที่จากบรรทัดที่เราเคยพิมพ์เอง ไปอยู่ในเกณฑ์ที่เราตั้งให้เครื่องมือใช้ตรวจแทน และคนที่ตั้งเกณฑ์เองไม่เป็น สุดท้ายก็คือคนที่ปล่อยให้ agent เป็นฝ่ายตั้งเกณฑ์ให้ตัวเอง
ที่มา: คลิป LIVE: Uncle Bob on Software Fundamentals in the Age of AI จากช่อง Matt Pocock
ชอบเรื่องแนวนี้ มีอีบุ๊คฟรีให้อ่านต่อ
NotebookLM ฉบับเข้าใจง่าย โยนเอกสารให้ AI อ่าน แล้วได้สรุป พอดแคสต์ และคลังความรู้ส่วนตัว
กดสมัครแล้วเราจะส่งเทคนิค AI และของแจกใหม่ๆ ให้ทางอีเมล เลิกรับได้ตลอด
สร้าง AI Automation Pipeline ทุกแบบ ด้วย Agents และ Skills

ปูจากพื้นฐาน prompt, context และ cost ไปจนปั้น Skill สั่ง Agent กับ Sub-agent แล้วต่อทุกอย่างเป็น pipeline อัตโนมัติที่ออกแบบเองได้ ดูฟรี 7 บทก่อนตัดสินใจ


