กฎของระบบอยู่ไฟล์ไหน
ร้านขายคอร์สออนไลน์ร้านหนึ่ง ให้สมาชิก Gold ลด 10% ไม่เกิน 500 บาท สัปดาห์นี้ร้านจะเพิ่มขายผ่าน LINE อัปเกรด Next.js เปลี่ยนชื่อคอลัมน์ใน DB และย้ายฐานข้อมูล ทุกงานเริ่มด้วยคำถามเดียวกัน กฎส่วนลดอยู่ไฟล์ไหน และ AI จะหาเจอไหม
SOLID ตอบว่าอะไรใช้แทนกันได้ Clean Architecture ตอบว่ากฎของระบบควรอยู่ตรงไหน อะไรส่งเข้าไปหามันได้ คุณเช็กมันได้โดยไม่เปิดเบราว์เซอร์ไหม และไฟล์ไหนเป็นคนต่อทุกอย่างเข้าด้วยกัน สำหรับคนที่ให้ AI เขียนโค้ด นี่คือเรื่องของ context: AI ต้องเปิดกี่ไฟล์เพื่อหากฎข้อเดียว ถ้าหาไม่เจอ มันจะเขียนใหม่จากความจำ แล้วร้านจะมีกฎส่วนลดสองเวอร์ชัน ร้านเดียวกันทั้งสี่กระดาน กดถัดไปแล้วนับว่าไฟล์ไหนต้องแก้ตาม และอะไรพังเงียบ ๆ บทจะถูกนับว่าเรียนจบเมื่อคุณไปถึงขั้นสุดท้ายของรูป
ร้านขายคอร์สออนไลน์ของคุณให้ส่วนลดสมาชิก Gold 10% สูงสุด 500 บาท ตอนนี้ขายผ่านหน้าเว็บอย่างเดียว แล้วคุณอยากขายผ่าน LINE bot ด้วย คุณสั่ง AI เขียนบอท
เพิ่มขายผ่าน LINE bot แล้วส่วนลด Gold ลืมเพดาน 500 บาท
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย มีไฟล์กฎส่วนลด และหน้าชำระเงินที่เรียกมัน ขวา ไม่มีชั้นกฎเลย กฎส่วนลดอยู่ในหน้าชำระเงินบนเว็บ ปนกับโค้ดที่วาดหน้าจอ
ซ้าย ลด 500 ตามกฎ ขวา ลด 500 เท่ากัน ตอนนี้กฎมีสำเนาเดียว เลยยังไม่มีอะไรต่างกัน
ซ้าย AI เพิ่มไฟล์บอท LINE ที่เรียกกฎส่วนลดเดิม ขวา AI เพิ่มไฟล์บอทเหมือนกัน แต่หากฎไม่เจอ เลยคิดส่วนลดใหม่จากความจำ ลืมเพดาน 500
ซ้าย ลด 500 เท่าเว็บ เพราะเรียกกฎเดียวกัน ขวา บอทลด 1,500 แต่เว็บลด 500 ขาดทุน 1,000 บาทต่อออเดอร์ และไม่มี test ไหนเทียบสองช่องทาง
กฎธุรกิจต้องอยู่ชั้นใน และไม่รู้จักอะไรจากชั้นนอก หน้าจอ บอท และ DB เป็นฝ่ายเรียกกฎ ไม่ใช่ที่เก็บกฎ
ถามก่อนรับ diffกฎในไฟล์นี้มีอยู่แล้วที่อื่นไหม ถ้ามี ทำไม AI ไม่เรียกของเดิม
The Dependency Rule (Martin, Clean Architecture 2017): source code dependencies must point only inward, toward higher-level policiesร้านเดิม ระบบจองที่นั่งคอร์ส สัปดาห์นี้มีสามงาน อัปเกรด Next.js รุ่นใหม่ที่เปลี่ยนวิธีอ่านหัวคำขอ เพิ่มการจองผ่าน LINE และเปลี่ยนชื่อคอลัมน์ระดับสมาชิกใน DB
เปลี่ยนชื่อคอลัมน์ใน DB แล้ว Gold ทุกคนจ่ายเต็มราคา โดยไม่มี error
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย กฎการจองที่นั่งรับคำขอธรรมดา และมีตัวแปลงสองไฟล์อยู่ชั้นนอก ขวา กฎการจองกับกฎอีก 5 เรื่อง รับคำขอของเว็บมาทั้งก้อน เปิด DB เอง แล้วอ่านชื่อคอลัมน์เอง
ซ้าย AI แก้ตัวรับคำขอจากเว็บบรรทัดเดียว เพราะวิธีอ่านหัวคำขอเปลี่ยน ชั้นกฎไม่รู้เรื่อง ขวา AI แก้กฎทั้ง 6 เรื่อง แล้วต้องทดสอบซ้ำทุกตัว
ซ้าย AI เพิ่มตัวรับคำขอจาก LINE ที่แปลงข้อความเป็นคำขอธรรมดาเอง ขวา LINE ไม่มีคำขอแบบเว็บให้ส่ง AI เลยปั้นคำขอเว็บปลอมขึ้นมา ไม่มีเลขสมาชิกอยู่ในนั้น จองได้แต่ไม่รู้ว่าใคร
ซ้าย AI แก้ตัวอ่านข้อมูลสมาชิกจาก DB ที่เดียว ขวา ชื่อคอลัมน์เก่าอ่านไม่เจอในกฎทั้ง 6 ไฟล์ Gold ทุกคนจ่ายเต็ม และไม่มี error สักบรรทัด
ของที่ข้ามเข้าไปหาชั้นใน ต้องเป็นข้อมูลธรรมดาในภาษาของเรา ชั้นนอกเป็นคนแปลงคำขอและแถวใน DB ให้ก่อน
ถามก่อนรับ diffกฎเรื่องนี้รับคำขอของเว็บ หรือแถวจาก DB เข้ามาตรง ๆ หรือเปล่า
Crossing boundaries (Martin 2017, ch. 22 “The Clean Architecture”): the data that crosses the boundaries is simple data structures; don't pass Entities or database rows acrossร้านของคุณจะขยายเพดานส่วนลด Gold จาก 500 เป็น 800 บาท AI แก้กฎแล้วต้องเช็กว่ายังถูก บนฝั่งขวา การเช็กหมายถึงเปิดเบราว์เซอร์ ล็อกอิน ใส่ของลงตะกร้า แล้วรอ Stripe โหมดทดสอบ
ขยายเพดานส่วนลดเป็น 800 แต่ต้องรอ test ผ่านเบราว์เซอร์ 95 วินาทีถึงจะรู้ว่าถูก
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย กฎส่วนลดอยู่ไฟล์เดียว มี test ของตัวเอง 12 เคส หน้าชำระเงินแค่วาดตัวเลข ขวา กฎอยู่ในหน้าชำระเงิน และเช็กได้ผ่านเบราว์เซอร์ทางเดียว
ซ้าย ผ่าน 12/12 ใน 8 ms AI รันทุกรอบเพราะมันเร็ว ขวา test ผ่านเบราว์เซอร์ผ่านใน 95 วินาที รันครั้งเดียว แล้วไม่มีใครรันอีก
ซ้าย AI แก้กฎกับเคสเพดานใน test แล้วทดสอบซ้ำ ผ่าน 12/12 ใน 8 ms ขวา AI แก้ในหน้าชำระเงิน แล้ว test ผ่านเบราว์เซอร์ต้องใช้ 95 วินาที AI รันหรือยัง ไม่มีใครรู้
ซ้าย AI พิมพ์เพดานกลับด้าน test ไม่ผ่าน 1 เคสภายใน 8 ms เลยไม่ได้ merge ขวา test ผ่านเบราว์เซอร์หมดเวลาแล้วถูกข้ามเป็นครั้งที่ 3 ของสัปดาห์ กฎที่ผิดขึ้นระบบไป Gold ลด 1,500 ทุกออเดอร์อยู่สามวัน
ส่วนที่ทดสอบยาก ให้มันโง่ที่สุด แล้วย้ายกฎไปอยู่ในส่วนที่ test ได้เร็ว test ที่เร็วพอ AI จะรันทุกรอบ
ถามก่อนรับ diffกฎที่ diff นี้แก้ มี test ที่รันเสร็จในไม่กี่ ms หรือต้องรอเปิดเบราว์เซอร์
Humble Object (Martin 2017, ch. 23; Meszaros, xUnit Test Patterns 2007): split behaviour into a hard-to-test humble part and a testable partร้านของคุณกำลังโต สัปดาห์นี้มีสามงาน เขียน test ให้การจองที่นั่ง ย้าย DB จาก Prisma ไป Drizzle และเพิ่มอีเมลต้อนรับ ทุกงานเริ่มที่คำถามเดียว คือของพวกนี้ถูกสร้างที่ไหน
ย้ายจาก Prisma ไป Drizzle ต้องไล่แก้ 23 ที่ ลืมที่เดียวก็พัง
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย จุดเริ่มแอปเปิด DB ต่อ Omise แล้วส่งให้ทั้งแอป ส่วนกฎการจอง กฎส่วนลด และบอท LINE รับของพวกนั้นมาใช้ ขวา ทั้ง 23 ไฟล์เปิด DB เอง รวมกฎส่วนลดด้วย
ซ้าย AI แก้จุดเริ่มตอน test บรรทัดเดียว ส่งของปลอมเข้าไปแทน ไม่ต้อง mock อะไร ขวา AI ต้อง mock DB ใน test ทั้ง 23 ไฟล์ ได้ของปลอม 23 ชุดที่ไม่มีชุดไหนเหมือนกัน
ซ้าย AI แก้จุดเริ่มแอปกับตัวต่อ DB ชั้นกฎไม่ถูกแตะ ขวา AI แก้ไป 22 ไฟล์ แต่ลืมบอท LINE การเชื่อมต่อ DB เก่าค้าง บอทตายอยู่สองวัน
ซ้าย AI เพิ่มการต่อบริการอีเมลที่จุดเริ่มแอปบรรทัดเดียว ขวา AI เพิ่มไฟล์อีเมลต้อนรับที่อ่านกุญแจตอนโหลดไฟล์ เครื่องทดสอบไม่มีกุญแจนั้น แอปเลยล้มตั้งแต่เปิด
ให้มีที่เดียวที่สร้างของและต่อทุกอย่างเข้าด้วยกัน ไฟล์อื่นรับของที่ต่อแล้วมาใช้ ไม่สร้างเอง
ถามก่อนรับ diffdiff นี้เปิด DB หรืออ่านค่าลับในไฟล์ที่ไม่ใช่จุดเริ่มแอปหรือเปล่า
The Main Component (Martin 2017, ch. 26): Main is the ultimate detail, the dirtiest component; it creates everything and hands it to the clean partsทั้งสี่ข้อคือรูปเดียวกัน มองจากไฟล์
พูดแบบคนสั่งงาน: กฎส่วนลดมีที่อยู่ที่เดียว ทุกช่องทางมาเรียก ไม่ใช่ต่างคนต่างเขียน (1) หน้าเว็บ บอท และ DB ต้องแปลงของตัวเองเป็นข้อมูลธรรมดาก่อนส่งเข้าหากฎ (2) กฎต้องเช็กได้ในไม่กี่ ms โดยไม่เปิดเบราว์เซอร์ ไม่งั้นไม่มีใครเช็ก (3) และมีไฟล์เดียวที่สร้างฐานข้อมูลกับ vendor แล้วส่งให้คนอื่นใช้ (4) Martin วาดเป็นวงกลมซ้อนกัน ลูกศรชี้เข้าหากฎเสมอ บนกระดาน: ไฟล์ที่เก็บกฎต้องไม่รู้จักชื่อของเฟรมเวิร์ก DB หรือ vendor เลย