การพัฒนาแบบ regeneration-first สำหรับแอปที่เปลี่ยนแปลงได้อย่างปลอดภัย
เรียนรู้แนวทาง regeneration-first เพื่อให้แอปปรับตัวได้ง่าย: เปลี่ยนข้อมูล ตรรกะ และ UI แล้วสร้างโค้ดใหม่ที่สะอาดแทนการแก้แบบแปะ ๆ

ทำไมการแก้แบบแปะ ๆ ถึงกลายเป็นหนี้สินทางเทคนิค
การแก้แบบแปะ (patching) เกิดขึ้นเมื่อมีความต้องการใหม่แล้วคุณยัดมันเข้าไปในแอปด้วยการแก้ไขเล็กที่สุดที่เป็นไปได้ มันดูเร็วเพราะมันเร็ว ปัญหาคือแต่ละแพตช์เป็นการแก้เฉพาะจุด และการแก้เฉพาะจุดไม่ค่อยสอดคล้องกับโครงสร้างที่แอปควรมี
เมื่อเวลาผ่านไป แพตช์เหล่านั้นจะทับถม แอปยังรันได้ แต่โค้ดและการตั้งค่าเริ่มไม่สอดคล้อง: ฐานข้อมูลบอกอย่างหนึ่ง UI ให้ความหมายอีกแบบ และกฎจริงๆ เก็บอยู่ในสามที่ต่างกัน ความไม่สอดคล้องนั้นคือหนี้สินทางเทคนิค มันไม่ใช่แค่ว่า 'โค้ดไม่ดี' แต่มันคือค่าใช้จ่ายที่เพิ่มขึ้นในการทำการเปลี่ยนแปลงครั้งถัดไป
คุณมักจะสังเกตเห็นได้จาก:
- ตรรกะยุ่งเหยิง จนนิ้วก้อยการเปลี่ยนกฎเล็กๆ ต้องแตะหลายหน้า/endpoint
- ฟิลด์ถูกคัดลอกซ้ำ ("status", "ticket_status", "status_v2") เพราะการเปลี่ยนชื่อรู้สึกลำบาก
- UI เปราะบาง มีการพึ่งพาโครงสร้างข้อมูลเฉพาะหรือกรณีพิเศษ
- ทางแก้ชั่วคราวกลายเป็นแฟลกที่ไม่เคยหายไป
- การแก้ไขต้องมีการแก้ตามมาเพราะไม่มีใครแน่ใจว่าจะมีส่วนไหนพังอีกบ้าง
ส่วนที่เจ็บปวดคือความเสี่ยงเติบโตเร็วแค่ไหน การเปลี่ยนที่ควรเล็ก (เพิ่มขั้นตอนอนุมัติ, ปรับกฎราคา, แยกระดับผู้ใช้หนึ่งเป็นสอง) กลับกลายเป็นการปล่อยที่เสี่ยงเพราะคุณคาดเดารัศมีผลกระทบไม่ได้ การทดสอบกลายเป็นการคาดเดา การย้อนกลับยากขึ้นเพราะแพตช์ไปแตะส่วนที่ไม่เกี่ยว
การพัฒนาแบบ regeneration-first เกิดมาเพื่อตอบโจทย์นี้ เป้าคือจัดโครงสร้างแอปให้การเปลี่ยนแปลงคาดเดาได้และถอยกลับได้ และให้แพลตฟอร์มสร้างโค้ดสะอาดขึ้นใหม่โดยไม่เอาฮัคเก่าไปต่อ
เป้าปฏิบัติได้คือ:
- แหล่งความจริงเดียวสำหรับข้อมูล (ไม่มีฟิลด์ซ้ำที่เหมือนกัน)
- กฎอยู่ที่เดียว ไม่กระจายไปใน UI และ endpoints
- UI เน้นการแสดงและรับข้อมูล ไม่ตัดสินใจทางธุรกิจ
- เปลี่ยนในโมเดลและตรรกะแล้ว regenerate แทนการแก้โค้ดมือ
แพลตฟอร์มอย่าง AppMaster สนับสนุนแนวทางนี้เพราะแอปถูกนิยามด้วยโมเดลและตรรกะแบบภาพ และแพลตฟอร์มสร้างซอสโค้ดเต็มรูปแบบ การ regenerate จะสะอาดก็ต่อเมื่อคุณหลีกเลี่ยงโครงสร้างที่เกิดจากการแก้แบบแปะตั้งแต่แรก
การพัฒนาแบบ regeneration-first หมายความว่าอย่างไร
การพัฒนาแบบ regeneration-first มองแอปของคุณเป็นชุดโมเดลที่ชัดเจน ไม่ใช่เป็นกองโค้ดที่ถูกแก้ด้วยมือ คุณแก้โมเดล, regenerate, แล้วได้เวอร์ชันแอปที่สดและสอดคล้อง จุดประสงค์คือปล่อยการเปลี่ยนแปลงโดยไม่ทิ้งฮัคที่ทำให้การเปลี่ยนครั้งถัดไปยากขึ้น
ในเวิร์กโฟลว์แบบ patch-first คำขอเล็กๆ (ฟิลด์สถานะใหม่, ขั้นตอนอนุมัติใหม่) ถูกเพิ่มตรงที่ใส่ได้เร็วสุด ใครสักคนแก้ handler ของ API, อัปเดตสกรีนหนึ่งหน้า, ใส่กรณีพิเศษที่อื่น แล้วก็เดินหน้า แอปยังทำงานได้วันนี้ แต่ตรรกะกระจัดกระจาย หลังจากผ่านไม่กี่รอบ ไม่มีใครแน่ใจว่ากฎจริงอยู่ที่ไหน
ด้วย regeneration-first แหล่งความจริงอยู่ที่โมเดล:
- โมเดลข้อมูล: เอนทิตี, ฟิลด์, ความสัมพันธ์, ข้อจำกัด
- โมเดลตรรกะทางธุรกิจ: กฎและโฟลว์ที่ตัดสินสิ่งที่จะเกิดขึ้น
- โมเดล UI: หน้าจอ, คอมโพเนนต์ และการผูกข้อมูล
ทุกอย่างที่ถูกสร้างจากโมเดลเหล่านั้น (API endpoints, การเข้าถึงฐานข้อมูล, โค้ดเว็บและมือถือ) เป็นผลลัพธ์ ไม่ใช่ที่สำหรับแก้ด่วน
ใน AppMaster เอาต์พุตนั้นอาจรวมถึง Go สำหรับ backend, Vue3 สำหรับเว็บ, และ Kotlin หรือ SwiftUI สำหรับมือถือ เมื่อความต้องการเปลี่ยน คุณแก้โมเดลครั้งเดียวแล้ว regenerate แทนการไล่หากฎเดียวกันในหลายไฟล์
วิธีนี้ช่วยให้แอปสอดคล้องข้ามเลเยอร์ เพราะคำนิยามเดียวขับเคลื่อนทุกส่วน: ถ้า 'Ticket Status' กลายเป็นฟิลด์ที่ต้องมี สคีมาฐานข้อมูล, การตรวจสอบ, API และการผูก UI ควรอัปเดตพร้อมกัน หากกฎการอนุมัติเปลี่ยน คุณแก้กระบวนการเพื่อให้ทุก endpoint และหน้าจอสะท้อนตรรกะเดียวกัน
การเปลี่ยนกรอบความคิดคือ: แก้สิ่งที่คุณหมายถึง (โมเดล), สร้างสิ่งที่คุณต้องการ (โค้ด)
สร้างโมเดลข้อมูลที่สามารถเติบโตได้
ถ้าคุณอยากให้ regeneration-first ใช้ได้ ให้เริ่มจากส่วนที่ควรเปลี่ยนน้อยที่สุด: โมเดลข้อมูล แอปที่รองรับการเปลี่ยนไม่รอดเพราะหน้าจอสมบูรณ์ทุกอย่าง แต่เพราะเอนทิตีหลักนิ่งและตั้งชื่อดี
เริ่มจากคำนามที่ธุรกิจจะยังใช้ในอีกหนึ่งปีข้างหน้า สำหรับหลายแอป นั่นคือ User, Account, Team, Ticket, Order, Invoice, Product หรือ Message เมื่อสิ่งเหล่านี้ชัด ทุกอย่างอื่น (workflow, permission, UI) มีฐานที่มั่นคง
การตั้งชื่อไม่ใช่เรื่องเล็ก มันป้องกันการเปลี่ยนภายหลังที่กลายเป็น migration และตรรกะที่แตกต่างกัน ใช้ชื่อเอนทิตีเอกพจน์, ใช้ชื่อฟิลด์สม่ำเสมอ (created_at แทน createdAt), และเลือกชนิดข้อมูลที่ตรงกับความจริง (เงินเป็น decimal, เวลาใช้ timezone ที่ตกลงกัน) ความไม่สอดคล้องเล็กๆ จะขยายไปสู่กฎ ตัวกรอง และรายงาน
วางแผนการเติบโตโดยไม่ overdesign คุณไม่จำเป็นต้องทำนายทุกฟิลด์ในอนาคต แต่ทำให้ประเภทการเปลี่ยนแปลงที่พบบ่อยปลอดภัยขึ้น:
- ชอบฟิลด์สถานะที่รับค่าใหม่ๆ ได้ แทนการสร้างตารางใหม่สำหรับทุกสถานะ
- ใช้ฟิลด์ที่เป็น optional สำหรับข้อมูลที่ไม่จำเป็นเสมอ (phone_number, external_id)
- ใส่ฟิลด์ audit ตั้งแต่เริ่ม (created_at, updated_at, created_by) เพื่อไม่ต้อง retrofit ทีหลัง
- แยก 'notes' และ 'metadata' ออกจากฟิลด์หลักเพื่อไม่ให้การทดลองปนเปื้อนโมเดลหลัก
ผู้ช่วยออกแบบข้อมูลแบบภาพช่วยให้มองเห็นความสัมพันธ์และข้อจำกัดก่อนจะกลายเป็นโค้ด ใน AppMaster Data Designer จะแมปสคีมาไปยัง PostgreSQL ทำให้คุณออกแบบตาราง ฟิลด์ และลิงก์ในที่เดียวแล้ว regenerate โค้ดสะอาดเมื่อความต้องการเปลี่ยน
ตัวอย่าง: พอร์ทัลซัพพอร์ตเริ่มด้วย Tickets ที่เชื่อมกับ Accounts และ Users ต่อมาธุรกิจขอ priority, category, และสถานะใหม่ 'Waiting on Customer' ถ้า Tickets มีฟิลด์สถานะและฟิลด์ optional สำหรับรายละเอียดอยู่แล้ว คุณสามารถเพิ่มค่าหรือฟิลด์ได้โดยไม่ต้องออกแบบฐานข้อมูลใหม่ แอปที่ regenerate จะคงคำสั่งค้นหาและ API ให้สอดคล้อง และคุณจะหลีกเลี่ยงกองแพตช์แบบครั้งเดียว
เป้าคือความอ่านง่ายวันนี้และความยืดหยุ่นในวันหน้า
ทำให้ตรรกะธุรกิจโมดูลและอ่านง่าย
ตรรกะธุรกิจคือจุดที่การเปลี่ยนมักทำให้พัง การแก้ด่วนที่ 'ใช้ได้วันนี้' อาจกลายเป็นตาข่ายของกรณีพิเศษในวันหน้า ด้วย regeneration-first คุณออกแบบตรรกะให้สามารถ regenerate ได้สะอาดโดยไม่พึ่งพาการแก้ที่เข้าใจได้เฉพาะคน
แนวปฏิบัติที่เป็นรูปธรรมคือมองแต่ละ workflow เป็นชุดของบล็อกเล็กๆ แต่ละบล็อกทำงานเดียว: ตรวจสอบอินพุต, คำนวณราคา, ตัดสินเส้นทาง, ส่งข้อความ, อัปเดตเรคอร์ด ใน AppMaster สิ่งนี้จับคู่กับ Business Process Editor ได้อย่างเป็นธรรมชาติ กระบวนการเล็กอ่านง่าย ทดสอบง่าย นำกลับมาใช้ใหม่ได้ และแทนที่ได้ง่าย
คิดในแง่ของอินพุตและเอาต์พุต
ก่อนสร้างบล็อก ให้จดสองอย่าง: มันต้องการอะไร และมันคืนค่าอะไร ถ้าคุณอธิบายไม่ได้ในประโยคเดียว บล็อกนั้นอาจทำงานมากเกินไป
บล็อกที่ดีมีขอบเขตชัดเจน รับอินพุตชัดเจน (user role, ticket status, order total) และคืนเอาต์พุตชัดเจน (approved หรือ denied, ราคาเรียบร้อย, ก้าวถัดไป) ความชัดเจนนี้ทำให้เปลี่ยนปลอดภัยเพราะคุณสามารถสลับบล็อกโดยไม่ต้องเดาว่าจะกระทบส่วนอื่นหรือไม่
เช็คลิสต์สั้น ๆ:
- หนึ่งวัตถุประสงค์ต่อบล็อก (เช่น validation หรือ calculation หรือ routing)
- อินพุตถูกส่งเข้า ไม่ใช่ 'หาเองจากที่ไหนสักแห่ง'
- เอาต์พุตถูกคืนค่า ไม่ซ่อนอยู่ในผลข้างเคียง
- ชื่อบล็อกสื่อผลลัพธ์ (เช่น
ValidateRefundRequest) - จัดการข้อผิดพลาดอย่างสม่ำเสมอ
หลีกเลี่ยงการพึ่งพาลับๆ
การพึ่งพาลับๆ ทำให้ตรรกะเปราะบาง ถ้า workflow พึ่งพาแฟลกทั่วระบบ, การเปลี่ยนสถานะเงียบ หรือ 'ตัวแปรนี้ถูกเซ็ตก่อนหน้านี้' การแก้เล็กๆ อาจเปลี่ยนพฤติกรรมโดยที่คุณคาดไม่ถึง
ส่งผ่านสถานะในกระบวนการอย่างตั้งใจ ถ้าต้องเก็บอะไร ให้เก็บในที่ชัดเจน (เช่นฟิลด์ในฐานข้อมูล) แล้วอ่านอย่างชัดเจน หลีกเลี่ยงพฤติกรรม 'เวทมนตร์' เช่นการเปลี่ยนเรคอร์ดในขั้นตอนหนึ่งแล้วสมมติอีกขั้นตอนจะเห็นการเปลี่ยนแปลงนั้น
ทำให้จุดตัดสินใจมองเห็นได้ เช่น พอร์ทัลซัพพอร์ตอาจแตกสาขาที่ 'เป็น VIP หรือไม่' และ 'นอกเวลาทำการหรือไม่' ถ้าสาขาเหล่านี้ชัดเจนและมีป้ายชื่อ การเปลี่ยนในอนาคตเช่น 'กฎ VIP เปลี่ยนในวันหยุดสุดสัปดาห์' จะเป็นการแก้ไขสั้น ๆ แทนการเขียนใหม่ที่เสี่ยง
แยกความกังวลของ UI ออกจากกฎและข้อมูล
แอปที่รองรับการเปลี่ยนง่ายสุดเมื่อ UI ยังคง 'บาง' หน้าจอควรเก็บข้อมูล แสดงสถานะ และนำทางผู้ใช้ เมื่อการตัดสินทางธุรกิจถูกซ่อนอยู่ในปุ่ม การตรวจสอบ และตรรกะหน้าจอครั้งเดียว ทุกความต้องการใหม่จะกลายเป็นแพตช์
ปฏิบัติต่อ UI เป็นเลเยอร์บางๆ เหนือกฎและข้อมูลร่วม แล้วแพลตฟอร์มจะสร้างการนำเสนอซ้ำได้สะอาดโดยไม่ต้องเขียนกฎซ้ำในสิบที่
จุดที่ UI หยุดและกฎธุรกิจเริ่ม
การแยกที่ใช้งานได้จริงคือ: UI จัดการความชัดเจน; ตรรกะธุรกิจจัดการความจริง UI สามารถจัดรูปแบบ ป้าย และช่วยผู้ใช้ได้ ตรรกะธุรกิจตัดสินว่าอนุญาตอะไรและจะเกิดอะไรต่อไป
ความรับผิดชอบของ UI มักรวมถึง:
- แสดงข้อมูลและเก็บอินพุตผู้ใช้
- การจัดรูปแบบ (วันที่, สกุลเงิน, รูปแบบโทรศัพท์)
- การตรวจสอบฟิลด์ที่จำเป็นอย่างพื้นฐาน (ว่าง vs ไม่ว่าง)
- แสดงข้อผิดพลาดที่ถูกส่งกลับจากตรรกะในภาษาที่เข้าใจง่าย
- การนำทางและเลย์เอาต์
กฎธุรกิจควรอยู่นอกหน้าจอ เช่น ใน workflow หรือ process editor: 'refund ต้องการอนุมัติจากผู้จัดการ', 'ลูกค้า VIP ข้ามคิว', 'ไม่สามารถปิด ticket ได้ถ้าไม่มีรหัสผลลัพธ์' เก็บกฎเหล่านี้ผูกกับโมเดลข้อมูล ไม่ใช่หน้าหนึ่งหน้าจอ
ออกแบบครั้งเดียว ใช้ซ้ำทั้งเว็บและมือถือ
ถ้าคุณรองรับไคลเอนต์มากกว่าหนึ่ง (เว็บและ native) การซ้ำจะทำให้เกิดการ drift ใช้คอมโพเนนต์ร่วมสำหรับรูปแบบทั่วไป (ป้ายสถานะ ticket, ตัวเลือก priority, การ์ดลูกค้า) แต่รักษาพฤติกรรมให้สอดคล้องด้วยการป้อนข้อมูลและผลลัพธ์กฎเดียวกันให้กับคอมโพเนนต์
ตัวอย่าง: คุณสามารถโมเดลสถานะ ticket ใน data designer ขับเคลื่อนการเปลี่ยนสถานะผ่าน business process เดียว แล้วให้ทั้งเว็บและมือถือเรียกกระบวนการนั้นและเรนเดอร์สถานะที่คืนมา เมื่อ 'Escalated' เปลี่ยนเป็น 'Urgent review' คุณแก้ครั้งเดียวแล้ว regenerate แทนการไล่หาสภาพซ่อนเร้นในแต่ละหน้าจอ
การทดสอบแบบง่าย: ถ้าคุณลบหน้าจอหนึ่งและสร้างใหม่พรุ่งนี้ แอปยังบังคับกฎเดิมได้หรือไม่ ถ้าได้ การแยกส่วนได้ผล
ขั้นตอนทีละขั้น: โครงสร้างแอปเพื่อการ regenerate ที่สะอาด
regeneration-first ทำงานได้ดีที่สุดเมื่อแอปของคุณแยกเป็นส่วนที่ชัดเจนและเปลี่ยนแยกกันได้ คิดแบบโมดูลก่อน ไม่ใช่หน้าจอ
ตั้งชื่อโมดูลหลักและเก็บแยกทั้งในหัวและในงาน: ข้อมูล (ตารางและความสัมพันธ์), กระบวนการ (ตรรกะ), API (endpoints), เว็บ UI, และมือถือ เมื่อความต้องการเปลี่ยน คุณควรชี้ได้ว่าส่วนไหนเปลี่ยนและส่วนไหนควรคงไว้
ลำดับการสร้างที่ยังรองรับการเปลี่ยน
ใช้วงจรเล็ก ๆ และให้แต่ละก้าวเรียบง่าย:
- ออกแบบข้อมูลก่อน: เอนทิตี, ฟิลด์, ความสัมพันธ์ที่ตรงกับความจริง
- เพิ่ม business processes เป็นโฟลว์ที่นำกลับมาใช้ได้ ทำให้แต่ละกระบวนการทำงานหนึ่งอย่าง (Create Ticket, Assign Agent, Close Ticket)
- เชื่อมกระบวนการกับ API endpoints หลังจากตรรกะอ่านได้ ช่วยให้ endpoints เป็น wrapper รอบ flow ไม่ใช่ที่ซ่อนกฎ
- สร้าง UI รอบงานผู้ใช้ ไม่ใช่รอบตารางฐานข้อมูล
- Regenerate และทดสอบหลังการเปลี่ยนเล็ก ๆ ทุกครั้ง
ตัวอย่างเล็ก: เปลี่ยนความต้องการโดยไม่เกิดแพตช์ยุ่งเหยิง
สมมติคุณสร้างพอร์ทัลซัพพอร์ตใน AppMaster เวอร์ชันแรกมี Tickets และ Comments สัปดาห์ต่อมาธุรกิจขอ Priority และกฎใหม่: ลูกค้า VIP เริ่มต้นเป็น High
ด้วยโครงสร้างโมดูล คุณแก้โมเดลข้อมูล (เพิ่ม Priority), อัปเดตกระบวนการ Create Ticket ให้ตั้งค่า Priority ตามประเภทลูกค้า, regenerate, และยืนยันว่าหน้าที่ UI เดิมยังทำงาน ไม่มีการแก้กระจายหลายหน้าจอ
นิสัยง่ายๆ ช่วยได้: หลังแต่ละครั้งที่ regenerate ให้รัน flow หลักแบบ end-to-end อย่างรวดเร็ว (create, update, permission check) ก่อนเพิ่มฟีเจอร์ถัดไป
ตัวอย่าง: พอร์ทัลซัพพอร์ตที่เปลี่ยนอยู่เรื่อยๆ
ลองนึกภาพพอร์ทัลซัพพอร์ตขนาดเล็ก ลูกค้าเข้าสู่ระบบ, ดู ticket ของตัวเอง, เปิด ticket เพื่อดูรายละเอียด และเพิ่มการตอบกลับ ตัวแทนซัพพอร์ตเห็น ticket เดียวกันบวกโน้ตภายใน
แนวทาง regeneration-first แยกสามอย่าง: โมเดลข้อมูล ticket, กระบวนการธุรกิจ (วิธีการที่ ticket เคลื่อน), และหน้าจอ UI เมื่อส่วนเหล่านี้ชัดเจน คุณแก้หนึ่งอย่างโดยไม่ต้องแพตช์รอบๆ
เริ่มง่าย แต่จัดโครงสร้างให้รองรับการเปลี่ยน
เวอร์ชันแรกอาจ minimal:
- ข้อมูล: Users, Tickets, Messages
- กระบวนการ: Create ticket, Reply, Assign to agent
- UI: รายการ ticket, รายละเอียด ticket, ฟอร์มสร้าง ticket
ใน AppMaster สิ่งนี้แมปกับโมเดลที่แบ็กด้วย PostgreSQL (Data Designer), workflow แบบลากวาง (Business Process Editor), และตัวสร้าง UI แยกสำหรับเว็บและมือถือ
การเปลี่ยน 1: เพิ่ม priority และวันที่ SLA
ฝ่ายผลิตขอ Priority (Low, Normal, High) และวันที่ครบ SLA ด้วยโครงสร้าง regeneration-first คุณเพิ่มฟิลด์ในโมเดล Ticket, แล้วอัปเดตเฉพาะที่อ่านหรือเขียนฟิลด์เหล่านั้น: กระบวนการ create-ticket ตั้งค่า default priority, หน้าจอ agent แสดงวันที่ SLA, และหน้ารายการเพิ่มตัวกรอง
แพลตฟอร์มจะ regenerate backend และ API ทำให้ฟิลด์ใหม่กลายเป็นส่วนหนึ่งของโค้ดอย่างเป็นทางการ
การเปลี่ยน 2: เพิ่มขั้นตอนอนุมัติก่อนปิด
ตอนนี้การปิด ticket ต้องการอนุมัติจากผู้จัดการสำหรับลูกค้าบางประเภท แทนที่จะแตกระเบิดกฎปิดไปในหลายหน้าจอ ให้เพิ่มสถานะชัดเจนในโมเดล (Open, Pending approval, Closed) และอัปเดตกระบวนการปิด:
- ตัวแทนขอปิด
- ระบบเช็คว่าต้องการอนุมัติหรือไม่
- ผู้จัดการอนุมัติหรือปฏิเสธ
- Ticket ปิดเมื่อได้รับการอนุมัติ
เพราะกฎอยู่ในกระบวนการเดียว UI จะแสดงสถานะปัจจุบันและการกระทำที่อนุญาตได้
การเปลี่ยน 3: การแจ้งเตือนแบบ push บนมือถือ
สุดท้าย ผู้ใช้ต้องการแจ้งเตือน push เมื่อมีตัวแทนตอบ อย่าฝังตรรกะการแจ้งเตือนไว้ในโค้ด UI ใส่มันไว้ในกระบวนการ 'New message': เมื่อบันทึกการตอบ ให้ทริกเกอร์โมดูลแจ้งเตือน แล้วการ regenerate จะผลิตแอปเนทีฟที่อัปเดตโดยไม่กลายเป็นงานแก้ไขแบบแมนนวล
ข้อผิดพลาดทั่วไปที่ทำลายเวิร์กโฟลว์แบบ regeneration-first
การพัฒนาแบบ regeneration-first ใช้ได้ก็ต่อเมื่อแอปของคุณยังคง regeneratable ทีมมักทำลายมันด้วยการแก้ด่วนที่ดูไม่เป็นไรวันนี้ แต่บังคับให้ต้องทำทางแก้ในวันหน้า
1) แก้โค้ดที่ถูกสร้างแทนการแก้โมเดล
การผสมส่วนที่ถูกสร้างกับการแก้ด้วยมือในที่ที่ถูก overwrite เป็นวิธีที่เร็วที่สุดในการสูญเสียการ regenerate ที่สะอาด ถ้าคุณใช้แพลตฟอร์มที่สร้างซอสโค้ดจริง ให้ถือโครงการเชิงภาพเป็นแหล่งความจริง เมื่อต้องการเปลี่ยน ให้แก้โมเดลข้อมูล, business process, หรือตัวสร้าง UI
กฎง่ายๆ: ถ้าคุณไม่สามารถ reproduce การเปลี่ยนโดยการ regenerate จากโครงการเชิงภาพ มันไม่ใช่การเปลี่ยนที่ปลอดภัย
2) ให้ UI ตัดสินกฎ
เมื่อหน้าจอเข้ารหัสกฎธุรกิจ ('ปุ่มนี้แสดงเฉพาะผู้ใช้ VIP', 'ฟอร์มนี้คำนวณยอดใน UI') ทุกหน้าจอกลายเป็นกรณีพิเศษ คุณจะได้ตรรกะที่ซ่อนยากต่อการอัปเดตอย่างสม่ำเสมอ
เก็บการตรวจสอบ สิทธิ์ และการคำนวณไว้ในตรรกะธุรกิจ (เช่น Business Process) แล้วให้ UI แสดงผล
3) ออกแบบโมเดลในฝันตั้งแต่เนิ่นๆ
การออกแบบเกินความจำเป็นดูเหมือนการเพิ่มฟิลด์ สถานะ และตารางเฉพาะกรณีมากมายก่อนมีการใช้งานจริง มันทำให้การเปลี่ยนเจ็บปวดเพราะการอัปเดตแตะหลายส่วน
เริ่มจากสิ่งที่รู้ แล้วขยายทีละน้อย:
- เพิ่มเฉพาะฟิลด์ที่อธิบายเป็นภาษาธรรมดาได้
- เก็บค่าสถานะสั้นและจริง (3-6 ค่า ไม่ใช่ 20)
- ถ้าจำเป็น ให้เพิ่มตารางใหม่ทีหลัง แทนยัดความหมายลงในตารางยักษ์
4) ข้ามคอนเวนชันการตั้งชื่อ
ชื่อไม่สอดคล้องสร้างโมเดลและ endpoints ที่สับสน: 'Cust', 'Customer', และ 'Client' ในแอปเดียว Regeneration ยังทำงานได้ แต่มนุษย์จะผิดพลาดขณะเปลี่ยนแปลง
เลือกพาทเทิร์นง่ายๆ ตั้งแต่ต้น (ชื่อตารางเอกพจน์, คำกริยาสม่ำเสมอสำหรับการกระทำ) และยึดตามมัน
5) สร้าง workflow ยักษ์เดียว
workflow ใหญ่หนึ่งอันรู้สึกเป็นระเบียบตอนแรก แต่ยากที่จะเปลี่ยนอย่างปลอดภัย แยกตรรกะเป็นกระบวนการเล็กๆ ที่มีอินพุตและเอาต์พุตชัดเจน ในพอร์ทัลซัพพอร์ต แยก 'Create ticket', 'Assign agent', และ 'Send notification' เพื่อแก้ทีละขั้นโดยไม่เสี่ยงส่วนที่เหลือ
ตรวจสอบด่วนก่อน regenerate และปล่อย
regeneration-first รู้สึกปลอดภัยก็ต่อเมื่อคุณมีรูทีนจับปัญหา 'แตกเงียบ' ก่อนปล่อย ก่อน regenerate ให้ทำรอบสั้นๆ ที่ตรงกับโครงสร้างแอปของคุณ: ข้อมูล, ตรรกะ, UI, และ API
เช็คลิสต์ด่วน:
- ข้อมูล: เอนทิตีและฟิลด์ตรงกับความต้องการปัจจุบัน ชื่อสอดคล้อง และคุณไม่ได้เก็บสองฟิลด์ที่หมายความเหมือนกัน
- ตรรกะ: แต่ละ workflow มีอินพุตชัดเจน เอาต์พุตชัดเจน และเส้นทางข้อผิดพลาดที่คาดเดาได้
- UI: หน้าจอใช้คอมโพเนนต์ร่วมและไม่เขียนกฎแบบฮาร์ดโค้ด
- API: endpoints แมปกับ workflow อย่างสม่ำเสมอ คุณตอบได้ว่า 'workflow ใดขับเคลื่อน endpoint นี้' โดยไม่ต้องค้นหา
- ปล่อย: คุณมีสคริปต์ทดสอบสั้น ๆ ที่ทำซ้ำได้ ไม่ใช่ 'คลิกจนดูดี'
เก็บแหล่งความจริงเดียวสำหรับกฎ ถ้าความสำคัญของ ticket ขึ้นกับระดับลูกค้า ให้กำหนดใน workflow เดียวแล้วให้ทั้ง API และ UI สะท้อนค่าเดียวกัน
สคริปต์ทดสอบ 10 นาทีที่จำลองการใช้งานจริงมักเพียงพอ:
- สร้างเรคอร์ดใหม่ด้วยฟิลด์จำเป็นเท่านั้น
- ทริกเกอร์ workflow หลักและยืนยันการเปลี่ยนสถานะที่คาดหวัง
- ลองเคสข้อผิดพลาดที่รู้จักหนึ่งกรณี (เช่นสิทธิ์ไม่พอหรือข้อมูลจำเป็นหาย)
- เปิดหน้าจอหลักบนเว็บและมือถือและยืนยันกฎเดียวกันแสดงแบบเดียวกัน
- เรียก endpoint หลักหนึ่งถึงสองตัวและยืนยันการตอบตรงกับที่ UI แสดง
ถ้าพบล้มเหลว ให้แก้โครงสร้างก่อน (ข้อมูล, workflow, UI ร่วม) แล้ว regenerate อีกครั้ง
ขั้นตอนถัดไป: นำแนวทางนี้ไปใช้กับการเปลี่ยนครั้งหน้า
เลือกหนึ่งจุดที่จะแก้ไขก่อนและให้ขอบเขตเล็ก ถ้าการเปลี่ยนที่ผ่านมาเจ็บปวด เริ่มจากส่วนที่ทำให้ต้องทำงานซ้ำมากที่สุด: โมเดลข้อมูล ชิ้นตรรกะที่ยุ่งเหยิง หรือหน้าจอที่ถูก 'ปรับอีกหน่อย' เสมอ
ปฏิบัติต่อการเปลี่ยนครั้งต่อไปเหมือนการซ้อม: ปรับ, regenerate, ยืนยัน, ปล่อย เป้าหมายคือให้การอัปเดตรู้สึกเป็นกิจวัตร ไม่ใช่ความเสี่ยง
ลูปง่ายๆ ที่ทำซ้ำได้:
- ทำการเปลี่ยนเล็กหนึ่งอย่าง (หนึ่งฟิลด์, หนึ่งกฎ, หรือพฤติกรรมหน้าจอหนึ่งอย่าง)
- Regenerate เพื่อให้โค้ดสอดคล้อง
- รันทดสอบแบบ smoke (happy path บวกหนึ่งกรณีกรอบ)
- ดีพลอยในสภาพแวดล้อมปลอดภัยก่อน (staging หรือ test workspace)
- ปล่อยและจดบันทึกสิ่งที่เรียนรู้
เก็บบันทึกการเปลี่ยนสั้น ๆ ที่อธิบายการตัดสินใจ ไม่ใช่แค่การแก้ เช่น: 'เราจัดเก็บ priority ของ ticket เป็น enum ไม่ใช่ free text เพื่อรายงานจะไม่พังเมื่อป้ายเปลี่ยน' สองบรรทัดแบบนี้ช่วยประหยัดเวลาได้มาก
ถ้าคุณอยากฝึกโดยไม่แก้โค้ดที่ถูกสร้าง ให้สร้างโมดูลเล็กๆ ใน AppMaster (เช่นฟอร์ม ticket, รายการ admin, หรือขั้นตอนอนุมัติง่ายๆ) regenerate หลังการเปลี่ยนแต่ละครั้ง แล้วสังเกตว่าการพัฒนาแอปง่ายขึ้นแค่ไหนเมื่อโมเดลเป็นแหล่งความจริง
การเปลี่ยนครั้งต่อไปของคุณเป็นเวลาที่ดีเริ่มต้น เลือกมุมหนึ่งของแอปแล้วทำให้มันรองรับการเปลี่ยนวันนี้
คำถามที่พบบ่อย
Patching คือการยัดฟีเจอร์ใหม่ด้วยการแก้ไขเล็กน้อยที่สุดที่ทำให้มันทำงานได้ทันที ซึ่งรู้สึกเร็วแต่บ่อยครั้งสร้างความไม่สอดคล้องกันระหว่างฐานข้อมูล, API, ตรรกะ และ UI ทำให้การเปลี่ยนแปลงครั้งถัดไปช้าลงและมีความเสี่ยงมากขึ้น。
Technical debt ในที่นี้หมายถึงต้นทุนเพิ่มเติมที่คุณต้องจ่ายเมื่อทำการเปลี่ยนแปลงในอนาคต เพราะโครงสร้างปัจจุบันรกหรือไม่สอดคล้องกัน มันแสดงออกมาเป็นเวลาพัฒนาเพิ่มขึ้น ความเสี่ยงของการเกิดบั๊กมากขึ้น และความจำเป็นในการทดสอบและประสานงานมากขึ้นสำหรับการเปลี่ยนแปลงที่ควรจะง่าย。
สัญญาณทั่วไปว่าแอปคุณเป็นแนว patch-first คือมีฟิลด์ซ้ำที่มีความหมายใกล้เคียงกัน, กฎทางธุรกิจกระจัดกระจายอยู่ใน UI และ endpoints, หรือมีแฟลก 'ชั่วคราว' ที่ไม่เคยถูกลบ นอกจากนี้การเปลี่ยนกฎเล็กๆ มักจะต้องแก้หลายที่เพราะไม่มีใครเชื่อขอบเขตของระบบ。
Regeneration-first หมายความว่าคุณแก้ไขโมเดลที่อธิบายแอป (ข้อมูล ตรรกะ UI) แล้วสร้างเอาต์พุตใหม่ (backend, API, client) จากนิยามเหล่านั้น เป้าหมายคือทำให้การเปลี่ยนแปลงคาดเดาได้เพราะแหล่งข้อมูลจริงอยู่ตรงกลางและสอดคล้องกัน。
ให้ถือว่าสโครงการเชิงภาพ (models และ processes) เป็นแหล่งข้อมูลจริง และโค้ดที่ถูกสร้างเป็นผลลัพธ์ ถ้าคุณแก้โค้ดที่ถูกสร้างตรงๆ คุณจะเสี่ยงต่อการสูญเสียการเปลี่ยนแปลงเมื่อ regenerate หรือกลายเป็นไม่กล้าทำ regeneration อีก ซึ่งจะทำให้กลับไปสู่แนว patch-first。
เริ่มจากคำนามที่คงที่ซึ่งธุรกิจจะยังใช้ต่อไป และตั้งชื่อให้ชัดเจน ใช้ชนิดข้อมูลที่ตรงกับความจริง ใส่ฟิลด์ audit ตั้งแต่ต้น และหลีกเลี่ยงการทำความหมายซ้ำในหลายฟิลด์เพื่อไม่ต้องมาทำ migration แก้ปัญหาในภายหลัง。
แยกตรรกะเป็นกระบวนการเล็กๆ แต่ละบล็อกทำหน้าที่เดียว กำหนดอินพุตและเอาต์พุตให้ชัด หากเปลี่ยนบล็อกใดบล็อกหนึ่ง คุณไม่ต้องเดาว่าส่วนอื่นจะเสียหายหรือไม่ ผ่านสถานะระหว่างขั้นตอนอย่างชัดเจนแทนการพึ่งพาแฟลกลับๆ หรือสถานะลึกลับ。
เก็บ UI ให้ทำหน้าที่แสดงและรับข้อมูล ส่วนกฎธุรกิจควรอยู่ในตรรกะร่วม (เช่น workflow หรือ process) ให้ UI แสดงผลว่าอนุญาตอะไรได้ แต่การตัดสินใจจริงๆ ควรอยู่ใน backend เพื่อป้องกันการกระจัดกระจายของกฎไปยังหน้าจอและไคลเอนต์ต่างๆ。
ลำดับที่ใช้งานได้จริงคือ: ออกแบบข้อมูล, สร้างกระบวนการที่อ่านง่าย, ห่อด้วย endpoints, แล้วสร้าง UI รอบงานผู้ใช้ หลังการเปลี่ยนเล็กๆ แต่ละครั้ง ให้ regenerate และรันทดสอบแบบ end-to-end สั้นๆ เพื่อจับปัญหาที่มองไม่เห็นก่อนจะสะสมขึ้น。
แนวทางนี้เหมาะเมื่อต้องเปลี่ยนบ่อยและต้องรองรับลูกค้าหลายแบบ (เว็บและ native) ให้สอดคล้อง หากต้องการวิธีไม่ใช้โค้ดเพื่อฝึกแนวทางนี้ AppMaster ให้คุณกำหนดโมเดลข้อมูล สร้างตรรกะแบบภาพ และสร้างซอสโค้ดจริงเพื่อไม่ต้องพึ่งพาแพตช์แบบครั้งเดียว


