Tech Blog

ไม่ให้ implementation ห่างจาก design — ตารางจัดการความเบี่ยงเบนและการตรวจสามจุดที่ปกป้อง 'ต้นฉบับ'

by Tech Writer
Design Documentation Operations Project Management Three-Way Cross-Check

สิ่งที่คุณจะได้เรียนรู้

  • วิธีจัดการ “ความเบี่ยงเบนของ design กับ implementation” โดยไม่ลบ design
  • รูปแบบตารางที่ผูก divergence ID (DVG-XXX) กับ ticket ID (TASK-XX)
  • วิธีสร้าง ตารางตรวจสามจุด ระหว่าง architectural / detailed / API design เพื่อตรวจหาความขัดแย้ง

กลุ่มเป้าหมาย

  • คนที่ทุกข์กับปัญหา “implementation วิ่งนำ design จนไม่ตรงกัน”
  • ผู้ที่ติดอยู่ระหว่าง cost ของการเขียน design ใหม่ทุกครั้ง กับ cost ของการปล่อยให้ design เน่า
  • ผู้ที่สนใจ การรัน validation command คู่ขนานกับการดำเนินงาน documentation

สภาพแวดล้อม

รายการเนื้อหา
รูปแบบ docMarkdown
โครงสร้าง repoe-scooter-sharing-doc จัดการเป็น repo แยก
Validationripgrep (rg)

บทความในชุด (อยู่ระหว่างการพัฒนา) — บทความนี้เป็นส่วนหนึ่งของ สร้าง E-Scooter Sharing ระดับท้องถนนตั้งแต่ศูนย์เพื่อทำความเข้าใจ — ชุดบันทึก Design, Implementation, และ Operations โปรเจกต์ยังดำเนินอยู่และมีการเพิ่มบทความและบันทึก design ใหม่อย่างต่อเนื่อง แรงจูงใจของชุด, technology stack, อภิธานศัพท์ screen ID, และดัชนีบทความถูกรวบรวมไว้ที่ลิงก์ด้านบน

บทนำ

ในโปรเจกต์ e-scooter sharing requirements / architectural / detailed / API design ถูกประกาศเป็น ต้นฉบับ ตั้งแต่เริ่มต้น

เมื่อ implementation คืบหน้า การเปลี่ยนหน้าจอ / endpoint ของ API / ชื่อ status ค่อย ๆ เบี่ยงเบน วิธีตอบสนองปกติคือ 2 ทาง:

  1. เขียน design ใหม่ทุกครั้ง → เจตนาการ design จางหาย
  2. ปล่อย design ไว้ → design กับ implementation เบี่ยงและทำให้สมาชิกใหม่สับสน

เราเลือก เส้นทางที่สาม

รักษาเนื้อหา design เป็นต้นฉบับ ตามรอย diff กับ implementation ปัจจุบันใน “ตารางจัดการความเบี่ยงเบน”

หน้า destination — รูปลักษณ์ implementation หลังจาก DVG-001〜DVG-004 ถึงสถานะ "แก้ไขแล้ว"

หมุดบนแผนที่คือร่างของ “ดึงจาก API ตาม design → แยกสี gray / dark blue ตามสถานะ” เราตามรอย divergence 4 ตัว (DVG-001〜DVG-004) ในตารางก่อนมาถึงตรงนี้


ตารางจัดการความเบี่ยงเบน (DVG-XXX)

สร้าง 30_user-app/002_detailed-design/divergence-register-(S01-S13).md และจัดการความเบี่ยงเบนแต่ละหน้าจอในตาราง

| DVG-ID | หน้าจอ | ค่าที่ design (ต้นฉบับ) | ความจริงของ implementation | ผลกระทบ | ทางแก้ชั่วคราว | TASK-ID | สถานะ |
|---|---|---|---|---|---|---|---|
| DVG-001 | S01 | ดึงข้อมูล master ของพอร์ตเริ่มต้นผ่าน GET /api/v1/ports | implement การดึงข้อมูลเริ่มต้นแล้ว | แก้ปัญหา latency ของการแสดงครั้งแรก | ไม่มี | TASK-A4 | แก้ไขแล้ว |
| DVG-002 | S07 | guard ของ S04 ส่งไป S07 เมื่อยังไม่ผ่านการทดสอบ | implement หน้าจอ / route / ส่ง API ของ S07 แล้ว | ลดความ dependence ของการดำเนินงาน | ไม่มี | TASK-B1 | แก้ไขแล้ว |

กฎ:

  • ห้ามลบ divergence อัปเดตได้แค่สถานะ (เปิด / กำลังดำเนินการ / แก้ไขแล้ว / ตกลงเปลี่ยน design)
  • ห้ามแตะเนื้อหา design (ต้นฉบับ) มีแค่ตาราง divergence ที่สะท้อนความจริง
  • เฉพาะ “ตกลงเปลี่ยน design” เท่านั้นที่เขียนทับฝั่งต้นฉบับได้

ผลตอบแทนของรูปแบบนี้: “ข้อเท็จจริงที่ implementation เบี่ยงจาก design” อยู่ใน doc เขียน design ใหม่ลบประวัติ ตาราง divergence เปลี่ยนเป็นบันทึก


รายการ Task ของ implementation diff (TASK-XX)

งานแก้ไข divergence จัดการใน 30_user-app/002_detailed-design/implementation-diff-tasks-(app-api).md เป็น ticket

### A-1 รวม state guard เข้าด้วยกัน
- เป้าหมาย: app
- เนื้อหา: implement RouteGuardService.determineNextRoute() และรวม logic การเปลี่ยน S05->S08->S09 มาที่จุดเดียว
- เงื่อนไขยอมรับ:
  - "ดำเนินการเตรียมขับ" ของ S04 ต้องผ่าน shared guard เสมอ
  - ลำดับการตัดสินตรงกับ design
- สถานะ: แก้ไขแล้ว (2026-05-23 / app commit: 34f9009)

เรียงตาม “ความสำคัญ A (แก้ไขเร็ว)” / “ความสำคัญ B (phase ถัดไป)” / “ความสำคัญ C (คุณภาพ)” โดย ticket ID อ้างอิงร่วมกับ divergence ID

การรายงานเสร็จสิ้น log เป็น “implementation” + “test” + “อัปเดต design” — ชุดสามชิ้น

เนื้อหา
ImplementationCommit ID (เช่น 34f9009)
Testหลักฐานเงื่อนไขยอมรับสำเร็จ (test ผ่าน, ตรวจหน้าจอ ฯลฯ)
อัปเดต designบันทึกการเปลี่ยนสถานะ divergence register เป็น “แก้ไขแล้ว”

การตรวจสามจุด (Architectural / Detailed / API)

สำหรับแต่ละหน้าจอ S01〜S13 สร้างตารางความสอดคล้องของ artifact 3 design

หน้าจอArchitecturalDetailedAPI designคำตัดสิน
S05OTP ส่ง / verifysendOtp(phone), verifyOtp(phone, code)POST /auth/send-otp, /auth/verify-otpOK
S10lock-toggletoggleLock(rentalId, requested)POST /rentals/{rental_id}/lock-toggleบางส่วน (อนาคต)

คำตัดสินใช้ “OK / บางส่วน / ต้องเปลี่ยน design / ต้องเปลี่ยน implementation” ทุกสิ่งที่ต่ำกว่า OK เต็มจะได้ divergence ID และไปอยู่ใน divergence register

"คืนที่นี่" ปิดใช้งาน — รูปลักษณ์ implementation ที่จับ "ปุ่มเปิดเมื่อมีช่องเท่านั้น" ของ design

ตัวอย่างเช่น “คืนที่นี่” ปิดใช้งานเมื่อพอร์ตเต็ม คือภาพของ architectural (“toggle ปุ่มตามความสามารถคืน”), API (“คืน available_empty_slots”), และ detailed (“logic เปิดปุ่มใน child sheet”) design ทั้งหมดมาถึงคำตัดสินเดียวกัน — OK — ในตารางสามจุดหลัง implementation

สิ่งสำคัญตรงนี้:

  • API design เป็นต้นฉบับระดับ string ของ endpoint
  • Architectural / detailed design เป็นต้นฉบับระดับ พฤติกรรมทางธุรกิจ
  • ตารางสามจุดคือพื้นที่ยืนยันว่า 3 แกนชี้ไปยังข้อเท็จจริงเดียวกัน

ถ้า implementation เปลี่ยนชื่อ /api/v1/foo/api/v1/bar ก่อนเขียน “ต้นฉบับ” ของ API design ใหม่ ต้องตกลงตารางสามจุดให้ตรงกับฝั่งอื่นด้วย นี่คือหัวใจของกลไกไม่ให้เบี่ยง


Validation Gate และ ripgrep

เมื่อ divergence register โต การเช็คด้วยมือว่า “แก้ไขครบหรือยัง” ไม่จริง validation gate แก้สิ่งนี้

# Gate A: นอก OLD ไม่มี path หลักที่ขาด prefix /api/v1
rg -n "`(GET|POST|PATCH|PUT|DELETE) /(auth|users|ports|scooters|rentals|payments|ekyc)" 00_common 20_API 30_user-app --glob "!**/OLD/**"

# Gate B: ตรวจการมีอยู่ของคำ "ต้องมี diff notation"
rg -n "implementation-diff-note|not-yet|future-impl" 30_user-app

# Gate C: ตรวจความตรงระหว่างตารางสามจุดกับ task list
rg -n "S10|partial|future-impl" 30_user-app/002_detailed-design

Gate A ตรวจหา path ของ API เก่าที่ยังอยู่ใน body — endpoint ที่ไม่มี prefix /api/v1

Gate B คือการตรวจครอบคลุม diff annotation ของรายการที่ยังไม่ implement

Gate C ยืนยันว่า คำตัดสินอย่าง “บางส่วน” หรือ “future-impl” ถูกใช้ความหมายเดียวกันทั้งใน divergence register และ task list

จะเข้า CI หรือรันด้วยมือก็ได้ สิ่งสำคัญคือ มี gate อยู่ และการละเลย design จะถูกตรวจพบ


OLD Directory สำหรับประวัติ

เมื่อจัดระเบียบ design อย่า “ลบ” design เก่า — ย้ายเข้า OLD/ directory

30_user-app/
├─ 001_architectural-design/
├─ 002_detailed-design/
│   ├─ OLD/
│   │   └─ old-payment-flow-design.md
│   ├─ payment-flow-design-(truth-aligned).md
│   └─ divergence-register-(S01-S13).md

--glob "!**/OLD/**" ของ Gate A ยกเว้น OLD ออกจากการค้นหา ตรวจเฉพาะ design ที่ยังมีชีวิต ประวัติยังอยู่ในมือ


บทเรียน

  • ความเบี่ยงเบนของ design / implementation จริงกว่าที่จะ “จัดการในรูปแบบที่เห็นได้” มากกว่า “กำจัดทิ้ง”
  • ตาราง divergence / task list / ตารางสามจุดเป็น 3 แกนที่มีบทบาทต่างกัน การอ้างอิงร่วมกันปกป้องต้นฉบับ
  • validation รันได้ดีในฐานะ gate เบา ๆ ที่ใช้ ripgrep (ไม่ต้องบังคับ CI)
  • ห้ามลบ design — ย้ายเข้า OLD เพื่อรักษาประวัติ

design ไม่ใช่ “ตำราที่ไม่มีวันผิด” แต่เป็น “เครื่องมือที่วางไว้กลางการตัดสินใจโดยถือว่าเป็นต้นฉบับ” กลไกเพื่อปกป้องสิ่งนี้ในโปรเจกต์เรา กลายเป็น divergence register และการตรวจสามจุด

ส่งข้อความได้ตามสบาย

กรุณาส่งข้อความ หากมีคำปรึกษาด้านเทคนิค ความคิดเห็น หรือคำถาม