แก้ไฟล์เดียว แล้วไฟล์ไหนต้องตามแก้
คุณไม่ได้พิมพ์โค้ดเองแล้ว แต่ทุกครั้งที่สั่ง AI แก้อะไรสักอย่าง คุณยังจ่ายอยู่อย่างหนึ่ง นึกถึงร้านค้าออนไลน์ที่อยากเปลี่ยนค่าส่งจาก 50 เป็น 40 บาท ถ้าค่าส่งอยู่ที่เดียว AI แก้บรรทัดเดียวแล้วจบ ถ้ามันถูกก๊อปไว้ทั้งในหน้าตะกร้า ใน LINE bot ในหน้า admin และในอีเมลยืนยันคำสั่งซื้อ AI ต้องเปิดสี่ที่ รัน test สี่ชุด แล้วอีเมลยืนยันก็ยังบอก 50 อยู่ เพราะไม่มีใครไปถึงที่ที่สี่ วงความเสียหายนี้คือ ไฟล์ที่ AI ต้องเปิด test ที่ต้องรันซ้ำ และบั๊กที่หลุดเพราะไม่มีใครอ่านไฟล์ที่ 40 SOLID คือห้าวิธีทำให้วงนั้นเล็กลง
ทุกรูปเริ่มจากคำขอจริงที่คุณเจอในงาน แล้วโชว์ระบบเดียวกันเป็นกระดานสองฝั่ง ซ้ายจัดไฟล์แบบที่ควรเป็น ขวาจัดแบบที่ AI เขียนให้เมื่อไม่มีใครบอก กดถัดไปเพื่อสั่งงานเดียวกันทั้งสองฝั่ง แล้วดูว่าฝั่งไหนต้องเปิดกี่ไฟล์ ทดสอบซ้ำกี่อย่าง และอะไรพัง ตอนจบคุณจะได้ลากขั้นตอนฝั่งขวาไปไว้ให้ถูกที่เอง บทจะถูกนับว่าเรียนจบเมื่อคุณไปถึงขั้นสุดท้ายของรูป
ระบบสมัครสมาชิกที่ให้ AI เขียนให้ตั้งแต่ต้น ใช้งานดีมาสามเดือน วันนี้คุณอยากเปลี่ยนข้อความอีเมลต้อนรับ พรุ่งนี้ฝ่ายบัญชีขอไฟล์รายชื่อแบบใหม่ คุณสั่ง AI ทีละงาน และบนฝั่งขวา ทุกงานเริ่มที่การเปิดไฟล์เดียวกัน
แก้แค่ข้อความอีเมลต้อนรับ แต่ไฟล์ที่ต้องเปิด มีโค้ดรหัสผ่านอยู่ด้วย
แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น↓ = ลำดับที่ระบบทำงาน= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ทั้งสองฝั่งทำงานเหมือนกัน ลูกค้ากดสมัคร ระบบตรวจรหัสผ่านแล้วบันทึก ส่งอีเมลต้อนรับ แล้วส่งรายชื่อให้บัญชี ต่างกันแค่โค้ดอยู่กี่ไฟล์ ซ้ายสามไฟล์ เรื่องละไฟล์ ขวาไฟล์เดียวที่ AI เขียนทุกอย่างไว้ด้วยกันตั้งแต่วันแรก
ซ้าย AI เปิดไฟล์อีเมล แก้จุดเดียว คุณทดสอบซ้ำแค่การส่งอีเมล ขวา AI แก้จุดเดียวเหมือนกัน แต่ไฟล์ที่เปิดคือไฟล์ที่มีรหัสผ่านอยู่ด้วย ทุกอย่างในนั้นต้องทดสอบซ้ำ รอบนี้ยังไม่มีอะไรพัง
ซ้าย AI เปิดไฟล์รายงาน แก้ไฟล์เดียว ขวา AI เปิดไฟล์เดิมอีกรอบ แก้เรื่องรายชื่อ แล้วถือโอกาสจัดโค้ดทั้งไฟล์ ส่วนเข้ารหัสรหัสผ่านหายไปในรอบนั้น คุณไม่ได้สั่งให้แตะเรื่องสมัครเลย
ซ้าย AI เพิ่มในไฟล์รายงาน ทดสอบซ้ำสองอย่างในไฟล์นั้น ขวา AI เปิดไฟล์เดิมเป็นรอบที่สาม ทดสอบซ้ำทั้งสามเรื่องอีกครั้ง และคราวนี้อีเมลต้อนรับพังตามไปด้วย
ไฟล์หนึ่งควรมีเรื่องเดียว ถ้าสั่งงานคนละเรื่องแล้ว AI เปิดไฟล์เดิมทุกครั้ง มันควรเป็นคนละไฟล์
ถามก่อนรับ diffdiff นี้แตะส่วนที่งานนี้ไม่ได้ขอหรือเปล่า และส่วนนั้นทำเรื่องอะไรอยู่
Single Responsibility (Martin, 2002/2014): a module should be responsible to one, and only one, actorแอปของคุณแจ้งเตือนลูกค้าทางอีเมล SMS และ LINE อยู่แล้ว สัปดาห์นี้คุณอยากได้ Slack สัปดาห์หน้า Discord แล้วตามด้วย push บนมือถือ บนฝั่งขวา ทุกงานเริ่มที่การสั่ง AI เปิดไฟล์เดิมไฟล์เดียว
เพิ่มแจ้งเตือนทาง Slack แต่ต้องเปิดแก้ไฟล์ที่อีเมล SMS และ LINE ใช้อยู่
แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย มีสัญญากลาง ไฟล์ที่เลือกช่องทางแล้วส่ง และไฟล์ของอีเมล SMS LINE อย่างละไฟล์ ขวา มีไฟล์เดียว ในนั้นมีสามกิ่ง ถ้าอีเมลทำอย่างนี้ ถ้า SMS ทำอย่างนั้น ถ้า LINE ทำอีกอย่าง
ซ้าย AI เพิ่มไฟล์ใหม่สำหรับ Slack ไฟล์อื่นไม่ถูกเปิด ขวา AI เพิ่มอีกกิ่งในฟังก์ชันเดิม แล้วคุณต้องทดสอบซ้ำทั้ง 4 กิ่ง รอบนี้ยังไม่พัง
ซ้าย AI เพิ่มไฟล์ Discord อีกไฟล์ ขวา AI เพิ่มอีกกิ่ง ทดสอบซ้ำ 5 กิ่ง แล้วกิ่งอีเมลพัง เพราะหลุดไปหนึ่งคำตอนแก้ ส่งไม่สำเร็จก็เงียบ
ซ้าย AI เพิ่มไฟล์ push ขวา AI เพิ่มอีกกิ่ง ทดสอบซ้ำ 6 กิ่ง แล้วกิ่ง SMS พัง เพราะมีเงื่อนไขเกินมาหนึ่งตัว
ซ้าย AI แก้สัญญากลาง แล้วไฟล์ช่องทางทั้ง 6 ไฟล์ใช้ไม่ได้ทันที ต้องแก้ตามทุกไฟล์ นี่คือราคาของสัญญากลาง ขวา เปิดไฟล์เดิมรอบที่สี่ แก้กิ่ง push แล้วทดสอบซ้ำทั้ง 6 กิ่งอีกรอบ
ของที่มีคนใช้อยู่แล้ว อย่าเปิดแก้ ของใหม่ให้เป็นไฟล์ใหม่ที่ทำตามแบบกลางเดิม แต่แบบกลางคือสัญญา เปลี่ยนเมื่อไร ทุกไฟล์ต้องแก้ตาม
ถามก่อนรับ diffช่องทางใหม่นี้เป็นไฟล์ใหม่ หรือเป็นอีกกิ่งในไฟล์ที่ทุกช่องทางใช้ร่วมกัน
Open-Closed (Meyer 1988, Martin 1996): open for extension, closed for modification — “ปิด” คือคำสัญญาต่อทุกอย่างที่ implement ไปแล้ว ไม่ใช่กุญแจล็อกไฟล์แอปของคุณส่งใบเสร็จทางอีเมลอยู่แล้ว ตอนนี้คุณอยากส่งทาง LINE, push และ SMS ด้วย AI เขียนตัวส่งใหม่ทีละตัว ทุกตัวรับของและส่งของกลับรูปแบบเดียวกับตัวเดิม
เพิ่มช่องส่งใบเสร็จทาง LINE หน้าตาเหมือนตัวเดิมทุกอย่าง แต่ทำตัวไม่เหมือน
แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย มีรูปแบบของตัวส่ง test สัญญา 7 ข้อ และตัวส่งทางอีเมลที่ผ่าน 7/7 ขวา มีขั้นตอนตัดเงินที่เรียกตัวส่ง กับตัวส่งทางอีเมลที่แค่คอมไพล์ผ่าน
ซ้าย ตัวส่ง LINE ผ่านแค่ 6/7 เพราะล้มทันทีตอน token หมดอายุ test เลยไม่ให้ merge ขวา คอมไพล์ผ่านแล้วปล่อยขึ้นระบบ ตีสอง ขั้นตอนตัดเงินล้มตรงส่งใบเสร็จ หลังตัดเงินไปแล้ว ลูกค้า 40 คนโดนตัดเงินแต่ไม่มีออเดอร์
ซ้าย ตัวส่ง push ผ่าน 7/7 แล้ว merge ขวา คอมไพล์ผ่านแล้วปล่อยขึ้นระบบเหมือนกัน รอบนี้ไม่พัง แต่เป็นเพราะโชคดี ไม่มีอะไรเช็ก
ซ้าย ตัวส่ง SMS ผ่าน 5/7 เพราะส่งซ้ำเองสามรอบเวลาเครือข่ายช้า test เลยไม่ให้ merge ขวา ปล่อยขึ้นระบบ แล้วลูกค้าได้ SMS ใบเสร็จสามรอบ รู้ตอนลูกค้าบ่น
ตัวใหม่ใช้แทนตัวเดิมได้ ก็ต่อเมื่อทำตัวเหมือนเดิม รับของส่งของรูปแบบตรงกันยังไม่พอ สัญญานั้นต้องเขียนเป็น test ที่ทุกตัวต้องผ่านเหมือนกัน
ถามก่อนรับ diffตัวใหม่นี้ผ่าน test ชุดเดียวกับตัวเดิมหรือยัง หรือแค่คอมไพล์ผ่าน
Liskov Substitution (Liskov 1987; Liskov & Wing 1994): subtypes must be substitutable for their base types without breaking the programคุณขอ AI เปลี่ยนวิธีรวมยอดในรายงานยอดขาย ต่อมาอยากเพิ่มตัวเล่นเสียงที่ต้องสตรีมไฟล์ แล้วตอนทำบัญชีอยากให้การคืนเงินบันทึกเหตุผลด้วย AI แก้ทีละงาน
เปลี่ยนแค่รายงานยอดขาย แต่ทุกไฟล์ที่รับคลังออเดอร์มาทั้งก้อนต้องทดสอบซ้ำ
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย มีช่องเล็กสามไฟล์ และคนใช้สามไฟล์ แต่ละไฟล์รับไปช่องเดียว ขวา มีคลังออเดอร์ก้อนเดียว 30 อย่าง และคนใช้สี่ไฟล์ที่รับมาทั้งก้อน รวมไฟล์แจ้งเตือนที่ไม่ได้ใช้เลย
ซ้าย AI แก้ช่องอ่านยอด แล้วทดสอบซ้ำแค่รายงาน ขวา AI แก้ในคลังก้อนเดียว แล้วคนใช้ทั้งสี่ไฟล์ต้องทดสอบซ้ำ mock ของคลังใน test ของแจ้งเตือน ยังรวมยอดแบบเก่า
ซ้าย AI เพิ่มช่องเล่นเสียงกับตัวเล่นเสียงเป็นไฟล์ใหม่ ไฟล์เดิมไม่รู้เรื่อง ขวา AI เพิ่มการสตรีมเข้าไปในคลังก้อนเดียว คนใช้ทั้งสี่ไฟล์ต้องทดสอบซ้ำ และรายงานรายสัปดาห์ล้มตอน deploy เพราะ mock ของคลังใน test ของรายงาน ไม่มีการสตรีม
ซ้าย AI แก้ช่องบันทึกการตัดเงิน แล้วแก้ตามแค่ตัดเงิน ขวา AI แก้ในคลังก้อนเดียว คนใช้ทั้งสี่ไฟล์ต้องทดสอบซ้ำอีกรอบ และการกันของในสต็อกพัง เพราะ mock เก่าใน test ของสต็อกยังคืนเงินแบบเดิม
อย่าให้ไฟล์ไหนต้องรับของที่มันไม่ได้ใช้ แบ่งช่องตามคนใช้ แล้วการแก้หนึ่งจุดจะกระทบแค่คนที่ใช้จุดนั้น
ถามก่อนรับ diffไฟล์นี้รับของมาทั้งก้อน แต่ใช้จริงกี่อย่าง
Interface Segregation (Martin 1996): clients should not be forced to depend on methods they do not useคุณจะย้ายจาก Stripe ไป Omise แล้วเพิ่ม 2C2P เป็นเจ้าที่สาม หลังจากนั้นอยากได้ test ให้กฎการตัดเงิน บนฝั่งขวา ทุกงานต้องเปิดไฟล์กฎการตัดเงิน
ย้ายจาก Stripe ไป Omise แล้วกฎลองใหม่ 3 ครั้งหายไปเงียบ ๆ
ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน
กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย
บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่
ซ้าย มีช่องตัดเงินที่กฎกำหนดเอง กับกฎการตัดเงินที่ไม่รู้จัก vendor เลย Stripe อยู่ในตัวแปลงของมัน ขวา มีกฎการตัดเงิน 5 ขั้นที่เรียก Stripe ตรง ๆ กับส่งใบเสร็จและรายงานยอดขายที่รู้จักรูปแบบข้อมูลของ Stripe
ซ้าย AI เพิ่มตัวแปลง Omise แล้วแก้จุดเลือกเจ้าตัดเงินบรรทัดเดียว ชั้นกฎไม่ถูกแตะ ขวา AI เขียนขั้นเรียก Stripe ใหม่ให้เป็น Omise ทดสอบซ้ำทั้ง 5 ขั้น และการลองใหม่ 3 ครั้งหายไปเงียบ ๆ
ซ้าย AI เพิ่มตัวแปลง 2C2P แล้วแก้จุดเลือกเจ้าอีกบรรทัด ขวา AI เปิดไฟล์กฎการตัดเงิน แก้ขั้นเรียก vendor อีกรอบ แล้วส่งใบเสร็จพัง เพราะ Omise ไม่มีลิงก์ใบเสร็จแบบ Stripe ใบเสร็จว่าง
ซ้าย AI เพิ่มตัวแปลงปลอมกับ test ของกฎ กฎธุรกิจมี test จริงครั้งแรก ขวา เขียน test ไม่ได้ เพราะทุก test ต้องยิง Stripe จริง และรายงานยอดขายพัง เพราะเลขรายการเป็นแบบ Omise อ่านไม่ออก แถวหายไป
กฎธุรกิจกำหนดช่องด้วยคำของตัวเอง vendor ทุกเจ้าต้องแปลงให้เข้าช่องนั้น เปลี่ยน vendor เมื่อไร กฎไม่ต้องรู้เรื่อง
ถามก่อนรับ diffไฟล์ที่มีกฎธุรกิจนี้เรียก vendor ตรง ๆ หรือเปล่า
Dependency Inversion (Martin 1996) และ Dependency Rule ใน Clean Architecture (2017): source dependencies point inward, toward policyทั้งห้าข้อคือเรื่องเดียวกัน
“จัดเรียงการพึ่งพากันของโค้ด ให้การแก้ที่จุดหนึ่ง ไม่บังคับให้ต้องแก้ที่อื่น” (Martin, Clean Architecture) พูดแบบคนสั่งงาน: ไฟล์ที่ถูกเปิดเพราะงานคนละเรื่อง ให้แยกเป็นเรื่องละไฟล์ (S) ของใหม่ให้เป็นไฟล์ใหม่ ไม่เปิดแก้ไฟล์ที่ทุกช่องทางใช้อยู่ (O) ของที่ใช้แทนกันได้ ต้องทำตัวเหมือนเดิม และมี test พิสูจน์ (L) ใครใช้อะไร ก็ให้เห็นแค่นั้น (I) และกฎธุรกิจเป็นคนกำหนดรูปแบบ ให้ vendor ปรับตัวเข้าหา ไม่ใช่กลับกัน (D)