TERRA RPC

กำลังเตรียมกระดาน…

Agent Dev Thai Academyprinciples · patterns · architecture
หลักสูตร · SOLID ในโค้ดจริง
0/6
SOLID ในโค้ดจริง · ห้าหลักการ บนกระดาน

แก้ไฟล์เดียว แล้วไฟล์ไหนต้องตามแก้

คุณไม่ได้พิมพ์โค้ดเองแล้ว แต่ทุกครั้งที่สั่ง AI แก้อะไรสักอย่าง คุณยังจ่ายอยู่อย่างหนึ่ง นึกถึงร้านค้าออนไลน์ที่อยากเปลี่ยนค่าส่งจาก 50 เป็น 40 บาท ถ้าค่าส่งอยู่ที่เดียว AI แก้บรรทัดเดียวแล้วจบ ถ้ามันถูกก๊อปไว้ทั้งในหน้าตะกร้า ใน LINE bot ในหน้า admin และในอีเมลยืนยันคำสั่งซื้อ AI ต้องเปิดสี่ที่ รัน test สี่ชุด แล้วอีเมลยืนยันก็ยังบอก 50 อยู่ เพราะไม่มีใครไปถึงที่ที่สี่ วงความเสียหายนี้คือ ไฟล์ที่ AI ต้องเปิด test ที่ต้องรันซ้ำ และบั๊กที่หลุดเพราะไม่มีใครอ่านไฟล์ที่ 40 SOLID คือห้าวิธีทำให้วงนั้นเล็กลง

ทุกรูปเริ่มจากคำขอจริงที่คุณเจอในงาน แล้วโชว์ระบบเดียวกันเป็นกระดานสองฝั่ง ซ้ายจัดไฟล์แบบที่ควรเป็น ขวาจัดแบบที่ AI เขียนให้เมื่อไม่มีใครบอก กดถัดไปเพื่อสั่งงานเดียวกันทั้งสองฝั่ง แล้วดูว่าฝั่งไหนต้องเปิดกี่ไฟล์ ทดสอบซ้ำกี่อย่าง และอะไรพัง ตอนจบคุณจะได้ลากขั้นตอนฝั่งขวาไปไว้ให้ถูกที่เอง บทจะถูกนับว่าเรียนจบเมื่อคุณไปถึงขั้นสุดท้ายของรูป

S
ตอนที่ 1 · ไฟล์หนึ่ง เรื่องเดียวแก้แค่ข้อความอีเมลต้อนรับ แต่ไฟล์ที่ต้องเปิด มีโค้ดรหัสผ่านอยู่ด้วย
เรื่องย่อ

ระบบสมัครสมาชิกที่ให้ AI เขียนให้ตั้งแต่ต้น ใช้งานดีมาสามเดือน วันนี้คุณอยากเปลี่ยนข้อความอีเมลต้อนรับ พรุ่งนี้ฝ่ายบัญชีขอไฟล์รายชื่อแบบใหม่ คุณสั่ง AI ทีละงาน และบนฝั่งขวา ทุกงานเริ่มที่การเปิดไฟล์เดียวกัน

ไฟล์นั้นมีส่วนที่ตรวจและเข้ารหัสรหัสผ่านอยู่ด้วย AI แก้เรื่องไฟล์รายชื่อ แล้ว “จัดระเบียบ” ทั้งไฟล์ไปพร้อมกัน ใน diff จะมีบรรทัดที่คุณไม่ได้สั่งให้แตะ ถ้ากดรับโดยไม่อ่าน มันจะขึ้นไปพร้อมงานรายชื่อ

ซ้าย งานสมัครสมาชิกถูกแยกเป็นสามไฟล์ เรื่องละไฟล์ ขวา AI เขียนทั้งสามเรื่องไว้ในไฟล์เดียวตั้งแต่วันแรก คุณจะสั่งงานเดิมสามงานให้ทั้งสองฝั่ง แล้วนับว่าฝั่งไหนต้องทดสอบซ้ำกี่เรื่อง และอะไรพังทั้งที่ไม่ได้สั่งแตะ

กล่องบนกระดานนี้ · 3
ส่วนทำอะไรทำไม
สมัครสมาชิกตรวจรหัสผ่าน เข้ารหัส แล้วบันทึกส่วนที่พังแล้วเจ็บที่สุด ควรมีเหตุผลให้เปิดน้อยที่สุด
อีเมลต้อนรับส่งอีเมลหลังสมัครเสร็จแก้ข้อความได้โดยไม่เปิดไฟล์ที่มีรหัสผ่าน
รายงานให้บัญชีส่งรายชื่อเป็น CSV และ Excelรูปแบบใหม่เพิ่มในไฟล์นี้ไฟล์เดียว
S
Single Responsibility · ไฟล์หนึ่ง เรื่องเดียวคุณจะสั่ง AI ว่าอะไร
S
Single Responsibility · ไฟล์หนึ่ง เรื่องเดียว
ตอนที่ 1 · สถานการณ์

ระบบสมัครสมาชิกที่ให้ AI เขียนให้ตั้งแต่ต้น ใช้งานดีมาสามเดือน วันนี้คุณอยากเปลี่ยนข้อความอีเมลต้อนรับ พรุ่งนี้ฝ่ายบัญชีขอไฟล์รายชื่อแบบใหม่ คุณสั่ง AI ทีละงาน และบนฝั่งขวา ทุกงานเริ่มที่การเปิดไฟล์เดียวกัน

แก้แค่ข้อความอีเมลต้อนรับ แต่ไฟล์ที่ต้องเปิด มีโค้ดรหัสผ่านอยู่ด้วย

แยกเป็นสามไฟล์ เรื่องละไฟล์
ไฟล์เดียว AI เขียนทุกอย่างไว้ในนั้น

แตะกล่องเพื่อหาไฟล์บนกระดาน

แยกเป็นสามไฟล์ เรื่องละไฟล์
ไฟล์เดียว AI เขียนทุกอย่างไว้ในนั้น
แยกเป็นสามไฟล์ เรื่องละไฟล์
ลูกค้ากดสมัคร
สมัครสมาชิกเรื่องเดียว
ตรวจรหัสผ่าน แล้วบันทึกสมาชิก
อีเมลต้อนรับเรื่องเดียว
ส่งอีเมลต้อนรับ
รายงานให้บัญชีเรื่องเดียว
ส่งรายชื่อให้บัญชี เป็น CSV
ไฟล์เดียว AI เขียนทุกอย่างไว้ในนั้น
ลูกค้ากดสมัคร
สมัครสมาชิกสามเรื่องในไฟล์เดียวมีรหัสผ่านอยู่ด้วย
ตรวจรหัสผ่าน แล้วบันทึกสมาชิกส่งอีเมลต้อนรับส่งรายชื่อให้บัญชี เป็น CSV
ขั้นสุดท้าย · ลากขั้นตอนมาแยกเป็นไฟล์ใหม่ตรงนี้+ ไฟล์ใหม่ลาก ส่งอีเมลต้อนรับ กับ ส่งรายชื่อให้บัญชี ออกจากไฟล์สมัครสมาชิก ไปเป็นไฟล์ของตัวเอง

กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น↓ = ลำดับที่ระบบทำงาน= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย

เขียว: ที่แก้หรือเพิ่มประ: ต้องทดสอบซ้ำ เพราะอยู่ไฟล์เดียวกับที่แก้แดง: พัง ทั้งที่ไม่มีใครแตะ

บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่

0 / 3
ก่อนเริ่มก่อนสั่งอะไร

ทั้งสองฝั่งทำงานเหมือนกัน ลูกค้ากดสมัคร ระบบตรวจรหัสผ่านแล้วบันทึก ส่งอีเมลต้อนรับ แล้วส่งรายชื่อให้บัญชี ต่างกันแค่โค้ดอยู่กี่ไฟล์ ซ้ายสามไฟล์ เรื่องละไฟล์ ขวาไฟล์เดียวที่ AI เขียนทุกอย่างไว้ด้วยกันตั้งแต่วันแรก

ขั้นที่ 1แก้ข้อความอีเมลต้อนรับ

ซ้าย AI เปิดไฟล์อีเมล แก้จุดเดียว คุณทดสอบซ้ำแค่การส่งอีเมล ขวา AI แก้จุดเดียวเหมือนกัน แต่ไฟล์ที่เปิดคือไฟล์ที่มีรหัสผ่านอยู่ด้วย ทุกอย่างในนั้นต้องทดสอบซ้ำ รอบนี้ยังไม่มีอะไรพัง

ขั้นที่ 2เปลี่ยนรูปแบบไฟล์รายชื่อให้บัญชี

ซ้าย AI เปิดไฟล์รายงาน แก้ไฟล์เดียว ขวา AI เปิดไฟล์เดิมอีกรอบ แก้เรื่องรายชื่อ แล้วถือโอกาสจัดโค้ดทั้งไฟล์ ส่วนเข้ารหัสรหัสผ่านหายไปในรอบนั้น คุณไม่ได้สั่งให้แตะเรื่องสมัครเลย

ขั้นสุดท้ายเพิ่มส่งรายชื่อเป็น Excel

ซ้าย AI เพิ่มในไฟล์รายงาน ทดสอบซ้ำสองอย่างในไฟล์นั้น ขวา AI เปิดไฟล์เดิมเป็นรอบที่สาม ทดสอบซ้ำทั้งสามเรื่องอีกครั้ง และคราวนี้อีเมลต้อนรับพังตามไปด้วย

หลักการ ในภาษาของโค้ด

ไฟล์หนึ่งควรมีเรื่องเดียว ถ้าสั่งงานคนละเรื่องแล้ว AI เปิดไฟล์เดิมทุกครั้ง มันควรเป็นคนละไฟล์

ถามก่อนรับ diffdiff นี้แตะส่วนที่งานนี้ไม่ได้ขอหรือเปล่า และส่วนนั้นทำเรื่องอะไรอยู่

Single Responsibility (Martin, 2002/2014): a module should be responsible to one, and only one, actor
O
ตอนที่ 2 · เพิ่มของใหม่ ไม่แก้ของเก่าเพิ่มแจ้งเตือนทาง Slack แต่ต้องเปิดแก้ไฟล์ที่อีเมล SMS และ LINE ใช้อยู่
เรื่องย่อ

แอปของคุณแจ้งเตือนลูกค้าทางอีเมล SMS และ LINE อยู่แล้ว สัปดาห์นี้คุณอยากได้ Slack สัปดาห์หน้า Discord แล้วตามด้วย push บนมือถือ บนฝั่งขวา ทุกงานเริ่มที่การสั่ง AI เปิดไฟล์เดิมไฟล์เดียว

ทุกครั้งที่ AI เพิ่มกิ่งใหม่ในฟังก์ชันเดิม มันแตะกิ่งเก่าไปด้วย รอบ Discord กิ่งอีเมลหลุดไปหนึ่งคำ ส่งไม่สำเร็จก็ไม่มีใครรู้ รอบ push มีเงื่อนไขเกินมาหนึ่งตัว กิ่ง SMS เลยถูกข้าม ใน diff มันดูเหมือนบรรทัดขยับนิดเดียว

ซ้าย ช่องทางแจ้งเตือนแต่ละช่องเป็นไฟล์ของตัวเอง ทุกไฟล์ทำตามแบบกลางอันเดียว ส่วนไฟล์ที่เลือกช่องทางแล้วส่ง ปิดไว้ ไม่ต้องแก้อีก ขวา ทุกช่องทางเป็นกิ่งในฟังก์ชันเดียว เราจะเพิ่ม Slack, Discord และ push ทีละตัว แล้วเปลี่ยนแบบกลางหนึ่งครั้ง

กล่องบนกระดานนี้ · 4
ส่วนทำอะไรทำไม
สัญญากลางของทุกช่องทางบอกว่าช่องทางหนึ่งต้องรับอะไร ส่งอะไรกลับเปลี่ยนเมื่อไร ทุกช่องทางต้องแก้ตาม
เลือกช่องทางแล้วส่งดูว่าลูกค้าใช้ช่องไหน แล้วส่งต่อให้ไฟล์ของช่องนั้นซ้ายไม่ต้องเปิดแก้อีก ขวาเปิดแก้ทุกรอบ
ส่งทางอีเมลช่องทางเดิมที่ใช้งานอยู่ไม่ต้องทดสอบซ้ำตอนเพิ่มช่องทางอื่น
ส่งทาง Slackช่องทางใหม่ หนึ่งช่อง หนึ่งไฟล์เพิ่มของใหม่โดยไม่แตะของเก่า
O
Open-Closed · เพิ่มของใหม่ ไม่แก้ของเก่าคุณจะสั่ง AI ว่าอะไร
O
Open-Closed · เพิ่มของใหม่ ไม่แก้ของเก่า
ตอนที่ 2 · สถานการณ์

แอปของคุณแจ้งเตือนลูกค้าทางอีเมล SMS และ LINE อยู่แล้ว สัปดาห์นี้คุณอยากได้ Slack สัปดาห์หน้า Discord แล้วตามด้วย push บนมือถือ บนฝั่งขวา ทุกงานเริ่มที่การสั่ง AI เปิดไฟล์เดิมไฟล์เดียว

เพิ่มแจ้งเตือนทาง Slack แต่ต้องเปิดแก้ไฟล์ที่อีเมล SMS และ LINE ใช้อยู่

ช่องทางละไฟล์ · เพิ่มใหม่ไม่แตะของเก่า
ทุกช่องทางในฟังก์ชันเดียว · เปิดแก้ทุกรอบ

แตะกล่องเพื่อหาไฟล์บนกระดาน

ช่องทางละไฟล์ · เพิ่มใหม่ไม่แตะของเก่า
ทุกช่องทางในฟังก์ชันเดียว · เปิดแก้ทุกรอบ
ช่องทางละไฟล์ · เพิ่มใหม่ไม่แตะของเก่า
ปิด ไม่มีใครเปิดแก้
สัญญากลางของทุกช่องทางสัญญากลางปิดแล้ว
รูปแบบการส่ง ที่ทุกช่องทางต้องทำตาม
เลือกช่องทางแล้วส่งปิดแล้ว
ลงทะเบียนช่องทางส่งตามช่องทางของลูกค้า
เปิดให้ต่อเติม
ส่งทางอีเมลช่องทางละไฟล์
ส่งอีเมล
ส่งทาง SMSช่องทางละไฟล์
ส่ง SMS
ส่งทาง LINEช่องทางละไฟล์
ส่ง LINE
ทุกช่องทางในฟังก์ชันเดียว · เปิดแก้ทุกรอบ
เลือกช่องทางแล้วส่งทุกช่องทางอยู่ในฟังก์ชันเดียว
ถ้าอีเมล ส่งอีเมลถ้า SMS ส่ง SMSถ้า LINE ส่ง LINE
ขั้นสุดท้าย · ลากขั้นตอนมาแยกเป็นไฟล์ใหม่ตรงนี้+ ไฟล์ใหม่ลากกิ่ง Slack, Discord, push ออกจากฟังก์ชันเดิม ไปเป็นไฟล์ของช่องทางนั้นเอง

กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย

เขียว: ที่แก้หรือเพิ่มประ: ต้องทดสอบซ้ำ เพราะอยู่ไฟล์เดียวกับที่แก้แดง: พัง ทั้งที่ไม่มีใครแตะ

บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่

0 / 4
ก่อนเริ่มก่อนแตะอะไร

ซ้าย มีสัญญากลาง ไฟล์ที่เลือกช่องทางแล้วส่ง และไฟล์ของอีเมล SMS LINE อย่างละไฟล์ ขวา มีไฟล์เดียว ในนั้นมีสามกิ่ง ถ้าอีเมลทำอย่างนี้ ถ้า SMS ทำอย่างนั้น ถ้า LINE ทำอีกอย่าง

ขั้นที่ 1เพิ่ม Slack

ซ้าย AI เพิ่มไฟล์ใหม่สำหรับ Slack ไฟล์อื่นไม่ถูกเปิด ขวา AI เพิ่มอีกกิ่งในฟังก์ชันเดิม แล้วคุณต้องทดสอบซ้ำทั้ง 4 กิ่ง รอบนี้ยังไม่พัง

ขั้นที่ 2เพิ่ม Discord

ซ้าย AI เพิ่มไฟล์ Discord อีกไฟล์ ขวา AI เพิ่มอีกกิ่ง ทดสอบซ้ำ 5 กิ่ง แล้วกิ่งอีเมลพัง เพราะหลุดไปหนึ่งคำตอนแก้ ส่งไม่สำเร็จก็เงียบ

ขั้นที่ 3เพิ่ม push

ซ้าย AI เพิ่มไฟล์ push ขวา AI เพิ่มอีกกิ่ง ทดสอบซ้ำ 6 กิ่ง แล้วกิ่ง SMS พัง เพราะมีเงื่อนไขเกินมาหนึ่งตัว

ขั้นสุดท้ายเปลี่ยนสัญญากลางที่ทุกช่องทางใช้ร่วมกัน

ซ้าย AI แก้สัญญากลาง แล้วไฟล์ช่องทางทั้ง 6 ไฟล์ใช้ไม่ได้ทันที ต้องแก้ตามทุกไฟล์ นี่คือราคาของสัญญากลาง ขวา เปิดไฟล์เดิมรอบที่สี่ แก้กิ่ง push แล้วทดสอบซ้ำทั้ง 6 กิ่งอีกรอบ

หลักการ ในภาษาของโค้ด

ของที่มีคนใช้อยู่แล้ว อย่าเปิดแก้ ของใหม่ให้เป็นไฟล์ใหม่ที่ทำตามแบบกลางเดิม แต่แบบกลางคือสัญญา เปลี่ยนเมื่อไร ทุกไฟล์ต้องแก้ตาม

ถามก่อนรับ diffช่องทางใหม่นี้เป็นไฟล์ใหม่ หรือเป็นอีกกิ่งในไฟล์ที่ทุกช่องทางใช้ร่วมกัน

Open-Closed (Meyer 1988, Martin 1996): open for extension, closed for modification — “ปิด” คือคำสัญญาต่อทุกอย่างที่ implement ไปแล้ว ไม่ใช่กุญแจล็อกไฟล์
L
ตอนที่ 3 · ชื่อเหมือน ต้องทำตัวเหมือนเพิ่มช่องส่งใบเสร็จทาง LINE หน้าตาเหมือนตัวเดิมทุกอย่าง แต่ทำตัวไม่เหมือน
เรื่องย่อ

แอปของคุณส่งใบเสร็จทางอีเมลอยู่แล้ว ตอนนี้คุณอยากส่งทาง LINE, push และ SMS ด้วย AI เขียนตัวส่งใหม่ทีละตัว ทุกตัวรับของและส่งของกลับรูปแบบเดียวกับตัวเดิม

ขั้นตอนตัดเงินทำงานเป็นลำดับ ตัดเงิน บันทึกยอด แล้วส่งใบเสร็จ มันเชื่อว่าตัวส่งจะแจ้งกลับเมื่อส่งไม่ได้ ไม่ล้มทันที และไม่ส่งซ้ำเอง ใน diff คุณเห็นแค่รูปแบบที่ตรงกัน พฤติกรรมที่ผิดสัญญาไม่โผล่ใน diff เลย

สองฝั่งมีตัวส่งใบเสร็จที่รับของเหมือนกัน ส่งของกลับเหมือนกัน ซ้ายมี test สัญญา 7 ข้อ ที่ตัวส่งทุกตัวต้องผ่านก่อน merge ขวามีแค่รูปแบบที่ตรงกัน กับขั้นตอนตัดเงินที่เชื่อว่าตัวส่งทุกตัวทำตัวเหมือนตัวเดิม เราจะเพิ่มตัวส่งใหม่สามตัว คือ LINE, push และ SMS

กล่องบนกระดานนี้ · 4
ส่วนทำอะไรทำไม
รูปแบบของตัวส่งใบเสร็จบอกแค่ว่ารับอะไร ส่งอะไรกลับบอกรูปแบบ ไม่ได้บอกพฤติกรรม
test สัญญาที่ตัวส่งทุกตัวต้องผ่านสัญญา 7 ข้อ เขียนเป็น test ชุดเดียวตัวส่งทุกตัวต้องผ่านชุดเดียวกันก่อน merge
ตัดเงิน แล้วส่งใบเสร็จตัดเงิน บันทึกยอด แล้วเรียกตัวส่งพังเมื่อตัวส่งใหม่ทำตัวไม่เหมือนเดิม
ส่งใบเสร็จทาง LINEตัวส่งตัวใหม่ล้มทันทีตอน token หมดอายุ แทนที่จะแจ้งกลับ
L
Liskov Substitution · ชื่อเหมือน ต้องทำตัวเหมือนคุณจะสั่ง AI ว่าอะไร
L
Liskov Substitution · ชื่อเหมือน ต้องทำตัวเหมือน
ตอนที่ 3 · สถานการณ์

แอปของคุณส่งใบเสร็จทางอีเมลอยู่แล้ว ตอนนี้คุณอยากส่งทาง LINE, push และ SMS ด้วย AI เขียนตัวส่งใหม่ทีละตัว ทุกตัวรับของและส่งของกลับรูปแบบเดียวกับตัวเดิม

เพิ่มช่องส่งใบเสร็จทาง LINE หน้าตาเหมือนตัวเดิมทุกอย่าง แต่ทำตัวไม่เหมือน

มี test สัญญา 7 ข้อ · ไม่ผ่านไม่ขึ้นระบบ
แค่คอมไพล์ผ่าน · ขึ้นระบบเลย

แตะกล่องเพื่อหาไฟล์บนกระดาน

มี test สัญญา 7 ข้อ · ไม่ผ่านไม่ขึ้นระบบ
แค่คอมไพล์ผ่าน · ขึ้นระบบเลย
มี test สัญญา 7 ข้อ · ไม่ผ่านไม่ขึ้นระบบ
รูปแบบของตัวส่งใบเสร็จรูปแบบกลาง
ส่งใบเสร็จ รูปแบบเดียวกันทุกตัว
test สัญญาที่ตัวส่งทุกตัวต้องผ่านสัญญา 7 ข้อ ที่ตัวส่งทุกตัวต้องผ่าน
ส่งสำเร็จ ต้องบอกว่าสำเร็จส่งไม่ได้ ต้องแจ้งกลับ ไม่ล้มทันทีห้ามส่งซ้ำเอง…อีก 4 ข้อ
ส่งใบเสร็จทางอีเมลผ่าน 7/7
ส่งใบเสร็จทางอีเมล
แค่คอมไพล์ผ่าน · ขึ้นระบบเลย
ตัดเงิน แล้วส่งใบเสร็จเชื่อว่าตัวส่งทุกตัวทำตัวเหมือนเดิม
บันทึกยอดที่ตัดไปส่งใบเสร็จให้ลูกค้า
ส่งใบเสร็จทางอีเมลแค่คอมไพล์ผ่าน
ส่งใบเสร็จทางอีเมล

กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น

เขียว: ไฟล์ใหม่ที่ผ่านสัญญาประแดง: ไม่ผ่าน test สัญญา · ไม่ได้ mergeแดง: พังบนระบบจริง ทั้งที่คอมไพล์ผ่าน

บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่

0 / 3
ก่อนเริ่มก่อนแตะอะไร

ซ้าย มีรูปแบบของตัวส่ง test สัญญา 7 ข้อ และตัวส่งทางอีเมลที่ผ่าน 7/7 ขวา มีขั้นตอนตัดเงินที่เรียกตัวส่ง กับตัวส่งทางอีเมลที่แค่คอมไพล์ผ่าน

ขั้นที่ 1เพิ่มตัวส่งทาง LINE

ซ้าย ตัวส่ง LINE ผ่านแค่ 6/7 เพราะล้มทันทีตอน token หมดอายุ test เลยไม่ให้ merge ขวา คอมไพล์ผ่านแล้วปล่อยขึ้นระบบ ตีสอง ขั้นตอนตัดเงินล้มตรงส่งใบเสร็จ หลังตัดเงินไปแล้ว ลูกค้า 40 คนโดนตัดเงินแต่ไม่มีออเดอร์

ขั้นที่ 2เพิ่มตัวส่งทาง push

ซ้าย ตัวส่ง push ผ่าน 7/7 แล้ว merge ขวา คอมไพล์ผ่านแล้วปล่อยขึ้นระบบเหมือนกัน รอบนี้ไม่พัง แต่เป็นเพราะโชคดี ไม่มีอะไรเช็ก

ขั้นสุดท้ายเพิ่มตัวส่งทาง SMS

ซ้าย ตัวส่ง 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
I
ตอนที่ 4 · ใครใช้อะไร เห็นแค่นั้นเปลี่ยนแค่รายงานยอดขาย แต่ทุกไฟล์ที่รับคลังออเดอร์มาทั้งก้อนต้องทดสอบซ้ำ
เรื่องย่อ

คุณขอ AI เปลี่ยนวิธีรวมยอดในรายงานยอดขาย ต่อมาอยากเพิ่มตัวเล่นเสียงที่ต้องสตรีมไฟล์ แล้วตอนทำบัญชีอยากให้การคืนเงินบันทึกเหตุผลด้วย AI แก้ทีละงาน

บนฝั่งขวา ทุกไฟล์ที่รับคลังออเดอร์มาทั้งก้อน ต้องคอมไพล์และทดสอบซ้ำ แม้ไฟล์แจ้งเตือนจะไม่ได้ใช้สักอย่าง ที่เจ็บกว่าคือ mock ที่เขียนมือใน test ตามไม่ทัน test ยังเขียว แต่ไปล้มตอน deploy

ซ้ายแบ่งคลังข้อมูลออเดอร์เป็นช่องเล็ก ๆ แต่ละไฟล์รับไปแค่ช่องที่ใช้ เช่น รายงานยอดขายเห็นแค่ช่องอ่านยอด ขวาคือคลังออเดอร์ก้อนเดียวที่ทำได้ 30 อย่าง และรายงาน สต็อก ตัดเงิน แจ้งเตือน รับมาทั้งก้อน เราจะแก้หนึ่งอย่าง เพิ่มหนึ่ง แล้วเปลี่ยนวิธีเรียกอีกหนึ่ง

กล่องบนกระดานนี้ · 4
ส่วนทำอะไรทำไม
ช่องอ่านยอดขายมีอย่างเดียว คือรวมยอดตามช่วงเวลารายงานเห็นแค่นี้ mock ก็เขียนแค่นี้
ช่องบันทึกการตัดเงินบันทึกยอดที่ตัด กับคืนเงิน ให้ขั้นตอนตัดเงินเปลี่ยนวิธีคืนเงิน แล้วกระทบแค่ตัดเงิน
คลังข้อมูลออเดอร์ ก้อนเดียวทำได้ 30 อย่างในที่เดียวแก้อย่างไหน ก็กระทบทุกไฟล์ที่รับมา
แจ้งเตือนรับคลังมาทั้งก้อนไว้เผื่อ ใช้จริง 0 อย่างต้องทดสอบซ้ำทุกครั้ง ทั้งที่ไม่ได้ใช้
I
Interface Segregation · ใครใช้อะไร เห็นแค่นั้นคุณจะสั่ง AI ว่าอะไร
I
Interface Segregation · ใครใช้อะไร เห็นแค่นั้น
ตอนที่ 4 · สถานการณ์

คุณขอ AI เปลี่ยนวิธีรวมยอดในรายงานยอดขาย ต่อมาอยากเพิ่มตัวเล่นเสียงที่ต้องสตรีมไฟล์ แล้วตอนทำบัญชีอยากให้การคืนเงินบันทึกเหตุผลด้วย AI แก้ทีละงาน

เปลี่ยนแค่รายงานยอดขาย แต่ทุกไฟล์ที่รับคลังออเดอร์มาทั้งก้อนต้องทดสอบซ้ำ

แต่ละไฟล์ได้แค่ช่องที่ใช้
ทุกไฟล์รับทั้ง 30 อย่าง

ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน

แต่ละไฟล์ได้แค่ช่องที่ใช้
ทุกไฟล์รับทั้ง 30 อย่าง
แต่ละไฟล์ได้แค่ช่องที่ใช้
ช่องเล็ก · แต่ละคนได้แค่ที่ใช้
ช่องอ่านยอดขายช่องเล็ก
รวมยอดตามช่วงเวลา
ช่องจองสต็อกช่องเล็ก
จองสต็อก
ช่องบันทึกการตัดเงินช่องเล็ก
บันทึกยอดที่ตัดคืนเงิน
คนใช้
รายงานยอดขายใช้แค่ช่องอ่านยอด
ใช้ ← ช่องอ่านยอดขาย
รายงานรายสัปดาห์
สต็อกใช้แค่ช่องจองสต็อก
ใช้ ← ช่องจองสต็อก
กันของไว้
ตัดเงินใช้แค่ช่องตัดเงิน
ใช้ ← ช่องบันทึกการตัดเงิน
ตัดเงินออเดอร์
ทุกไฟล์รับทั้ง 30 อย่าง
คนใช้ · รับมาทั้งก้อน
รายงานยอดขายใช้จริง 2 อย่าง
ใช้ ← คลังออเดอร์ทั้งก้อน
รายงานรายสัปดาห์
สต็อกใช้จริง 1 อย่าง
ใช้ ← คลังออเดอร์ทั้งก้อน
กันของไว้
ตัดเงินใช้จริง 3 อย่าง
ใช้ ← คลังออเดอร์ทั้งก้อน
ตัดเงินออเดอร์
แจ้งเตือนใช้จริง 0 อย่างรับไว้เผื่อ
ใช้ ← คลังออเดอร์ทั้งก้อน
ส่งแจ้งเตือน
คลังข้อมูลออเดอร์ ก้อนเดียว30 อย่างในก้อนเดียว
รวมยอดตามช่วงเวลาจองสต็อกบันทึกยอดที่ตัดคืนเงินที่นั่งที่เหลือ…อีก 25 อย่าง
ขั้นสุดท้าย · ลากขั้นตอนมาแยกเป็นไฟล์ใหม่ตรงนี้+ ไฟล์ใหม่ลาก รวมยอด จองสต็อก บันทึกยอด คืนเงิน ออกจากคลังก้อนเดียว ไปเป็นช่องเล็กของตัวเอง

กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย

เขียว: ที่แก้หรือเพิ่มประ: ต้องแก้ตามหรือทดสอบซ้ำ เพราะรับของที่เปลี่ยนมาใช้แดง: พัง เพราะ mock ใน test ตามไม่ทัน

บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่

0 / 3
ก่อนเริ่มก่อนแตะอะไร

ซ้าย มีช่องเล็กสามไฟล์ และคนใช้สามไฟล์ แต่ละไฟล์รับไปช่องเดียว ขวา มีคลังออเดอร์ก้อนเดียว 30 อย่าง และคนใช้สี่ไฟล์ที่รับมาทั้งก้อน รวมไฟล์แจ้งเตือนที่ไม่ได้ใช้เลย

ขั้นที่ 1เปลี่ยนวิธีรวมยอดในรายงาน

ซ้าย AI แก้ช่องอ่านยอด แล้วทดสอบซ้ำแค่รายงาน ขวา AI แก้ในคลังก้อนเดียว แล้วคนใช้ทั้งสี่ไฟล์ต้องทดสอบซ้ำ mock ของคลังใน test ของแจ้งเตือน ยังรวมยอดแบบเก่า

ขั้นที่ 2เพิ่มตัวเล่นเสียง ต้องสตรีมไฟล์ได้

ซ้าย 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
D
ตอนที่ 5 · กฎเป็นคนกำหนด ไม่ใช่ vendorย้ายจาก Stripe ไป Omise แล้วกฎลองใหม่ 3 ครั้งหายไปเงียบ ๆ
เรื่องย่อ

คุณจะย้ายจาก Stripe ไป Omise แล้วเพิ่ม 2C2P เป็นเจ้าที่สาม หลังจากนั้นอยากได้ test ให้กฎการตัดเงิน บนฝั่งขวา ทุกงานต้องเปิดไฟล์กฎการตัดเงิน

ในไฟล์นั้น บรรทัดที่บอกว่าลองใหม่กี่ครั้ง อยู่ติดกับบรรทัดที่เรียก Stripe AI เขียนสองเรื่องนี้ใหม่พร้อมกัน แล้วการลองใหม่หายไปเงียบ ๆ ใน diff มันดูเหมือนแค่การเปลี่ยนเจ้าตัดเงิน

ซ้าย กฎการตัดเงินรู้จักแค่ช่องตัดเงินที่มันกำหนดเอง ส่วน Stripe อยู่ในตัวแปลงของมันเอง ขวา กฎการตัดเงินเรียก Stripe ตรง ๆ และกฎธุรกิจอย่าง บัตรไม่ผ่านให้ลองใหม่ 3 ครั้ง อยู่ปนกับโค้ดของ vendor เราจะย้ายไป Omise เพิ่ม 2C2P แล้วเขียน test ให้กฎการตัดเงิน

กล่องบนกระดานนี้ · 5
ส่วนทำอะไรทำไม
ช่องตัดเงินที่กฎกำหนดเองบอกว่าตัดเงินต้องรับอะไร คืนอะไร ด้วยคำของเราเองไม่มีรูปแบบของ vendor ข้ามเข้ามา
กฎการตัดเงินตัดเงินออเดอร์ และลองใหม่ 3 ครั้งถ้าบัตรไม่ผ่านเปลี่ยน vendor แล้วไฟล์นี้ไม่ถูกแตะ
ตัวแปลง Stripeแปลง Stripe ให้เข้าช่องที่กฎกำหนดvendor หนึ่งเจ้า ไฟล์หนึ่งไฟล์
จุดเลือกเจ้าตัดเงินเลือกว่าจะใช้เจ้าไหนเปลี่ยน vendor คือแก้บรรทัดเดียว
ตัวแปลงปลอมสำหรับ testตอบกลับตามที่ test กำหนดทดสอบกฎธุรกิจได้โดยไม่ยิง Stripe จริง
D
Dependency Inversion · กฎเป็นคนกำหนด ไม่ใช่ vendorคุณจะสั่ง AI ว่าอะไร
D
Dependency Inversion · กฎเป็นคนกำหนด ไม่ใช่ vendor
ตอนที่ 5 · สถานการณ์

คุณจะย้ายจาก Stripe ไป Omise แล้วเพิ่ม 2C2P เป็นเจ้าที่สาม หลังจากนั้นอยากได้ test ให้กฎการตัดเงิน บนฝั่งขวา ทุกงานต้องเปิดไฟล์กฎการตัดเงิน

ย้ายจาก Stripe ไป Omise แล้วกฎลองใหม่ 3 ครั้งหายไปเงียบ ๆ

กฎตัดเงินแยกจาก Stripe
กฎตัดเงินกับ Stripe ปนไฟล์เดียว

ลูกศร = ไฟล์ไหนพึ่งไฟล์ไหน · เขียว ถูกทิศ · แดง ชี้ผิดทาง · แตะกล่องเพื่อหาไฟล์บนกระดาน

กฎตัดเงินแยกจาก Stripe
กฎตัดเงินกับ Stripe ปนไฟล์เดียว
กฎตัดเงินแยกจาก Stripe
ชั้นกฎ · กฎธุรกิจ
ช่องตัดเงินที่กฎกำหนดเองกฎเป็นคนกำหนดช่อง
ตัดเงิน รับยอดกับบัตร คืนผล
กฎการตัดเงินไม่รู้จัก vendor
ใช้ ← ช่องตัดเงินที่กฎกำหนดเอง
ตัดเงินออเดอร์บัตรไม่ผ่าน ลองใหม่ได้ 3 ครั้ง
ชั้นต่อ vendor
ตัวแปลง Stripeแปลง Stripe ให้เข้าช่อง
แปลงให้ Stripe
จุดเลือกเจ้าตัดเงินบรรทัดเดียว
เลือกว่าใช้เจ้าไหน
กฎตัดเงินกับ Stripe ปนไฟล์เดียว
กฎการตัดเงินกฎธุรกิจ + vendor ปนกัน
ใช้ ← Stripe โดยตรง
ตัดเงินออเดอร์บัตรไม่ผ่าน ลองใหม่ได้ 3 ครั้งเรียก Stripe ตัดเงินดึงลิงก์ใบเสร็จจาก Stripeส่งเลขรายการแบบ Stripe ให้รายงาน
ส่งใบเสร็จรู้จักรูปแบบของ Stripe
ใช้ ← รูปแบบข้อมูลของ Stripe
ส่งใบเสร็จให้ลูกค้า
รายงานยอดขายอ่านเลขรายการแบบ Stripe
อ่านเลขรายการแบบ Stripe
ขั้นสุดท้าย · ลากขั้นตอนมาแยกเป็นไฟล์ใหม่ตรงนี้+ ไฟล์ใหม่ลาก เรียก Stripe ตัดเงิน ดึงลิงก์ใบเสร็จ และส่งเลขรายการแบบ Stripe ไปไว้ในตัวแปลง Stripe

กล่อง = ไฟล์หนึ่งไฟล์ ที่ AI ต้องเปิดเมื่อแก้เรื่องในนั้นขั้นตอนชิ้น = สิ่งที่โค้ดทำ หนึ่งขั้น= ชิ้นที่ลากไปไฟล์ใหม่ได้ ในขั้นสุดท้าย

เขียว: ที่แก้หรือเพิ่มประ: ต้องทดสอบซ้ำ เพราะอยู่ไฟล์เดียวกับที่แก้แดง: พัง ทั้งที่ไม่มีใครแตะ

บนมือถือ: แตะที่กระดานเพื่อไปขั้นถัดไป ขั้นสุดท้ายแตะชิ้นที่ลากได้ แล้วแตะกล่องที่มันควรอยู่

0 / 3
ก่อนเริ่มก่อนแตะอะไร

ซ้าย มีช่องตัดเงินที่กฎกำหนดเอง กับกฎการตัดเงินที่ไม่รู้จัก vendor เลย Stripe อยู่ในตัวแปลงของมัน ขวา มีกฎการตัดเงิน 5 ขั้นที่เรียก Stripe ตรง ๆ กับส่งใบเสร็จและรายงานยอดขายที่รู้จักรูปแบบข้อมูลของ Stripe

ขั้นที่ 1ย้ายไป Omise

ซ้าย AI เพิ่มตัวแปลง Omise แล้วแก้จุดเลือกเจ้าตัดเงินบรรทัดเดียว ชั้นกฎไม่ถูกแตะ ขวา AI เขียนขั้นเรียก Stripe ใหม่ให้เป็น Omise ทดสอบซ้ำทั้ง 5 ขั้น และการลองใหม่ 3 ครั้งหายไปเงียบ ๆ

ขั้นที่ 2เพิ่ม 2C2P

ซ้าย AI เพิ่มตัวแปลง 2C2P แล้วแก้จุดเลือกเจ้าอีกบรรทัด ขวา AI เปิดไฟล์กฎการตัดเงิน แก้ขั้นเรียก vendor อีกรอบ แล้วส่งใบเสร็จพัง เพราะ Omise ไม่มีลิงก์ใบเสร็จแบบ Stripe ใบเสร็จว่าง

ขั้นสุดท้ายเขียน test ให้กฎการตัดเงิน

ซ้าย 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
ครบห้ารูปแล้ว? ทายตัวเลขก่อนสั่ง AIแบบทดสอบแปดข้อ ทุกข้อคือคำสั่งที่กำลังจะส่งให้ AI
ทำแบบทดสอบ →

ทั้งห้าข้อคือเรื่องเดียวกัน

“จัดเรียงการพึ่งพากันของโค้ด ให้การแก้ที่จุดหนึ่ง ไม่บังคับให้ต้องแก้ที่อื่น” (Martin, Clean Architecture) พูดแบบคนสั่งงาน: ไฟล์ที่ถูกเปิดเพราะงานคนละเรื่อง ให้แยกเป็นเรื่องละไฟล์ (S) ของใหม่ให้เป็นไฟล์ใหม่ ไม่เปิดแก้ไฟล์ที่ทุกช่องทางใช้อยู่ (O) ของที่ใช้แทนกันได้ ต้องทำตัวเหมือนเดิม และมี test พิสูจน์ (L) ใครใช้อะไร ก็ให้เห็นแค่นั้น (I) และกฎธุรกิจเป็นคนกำหนดรูปแบบ ให้ vendor ปรับตัวเข้าหา ไม่ใช่กลับกัน (D)

ห้ากระดานมาจากงานจริงห้าชิ้น: แก้อีเมลต้อนรับ · เพิ่มช่องทางแจ้งเตือน · เพิ่มช่องส่งใบเสร็จ · แก้รายงานยอดขาย · ย้ายเจ้าระบบตัดเงินคีย์บนกระดาน: Enter / คลิก = ขั้นถัดไป · ⌫ = รีเซ็ต