Build Log
ลูกค้าขอแค่เว็บแสดงสินค้า — แต่ข้อมูลจริงอยู่ใน POS อายุ 30 ปี
โดย chaninl ·
โจทย์ตอนแรกฟังดูง่ายมากครับ — เว็บที่แสดงสินค้าของลูกค้า แบบที่นั่งประชุมรอบแรก แล้วพยักหน้าตามได้เลย เริ่มสเก็ตช์หน้าเว็บในหัวไปแล้วด้วยซ้ำ
แล้วจุดหักมุมก็มา ข้อมูลสินค้าทั้งหมดอยู่บนคอมพิวเตอร์ที่ออฟฟิศของลูกค้า มันถูก generate จาก script ที่ไปดึงข้อมูลจากระบบ Visual FoxPro — ใช่ครับ FoxPro — ทุกๆ 1 ชั่วโมง และผมจะไม่มีทางได้แตะ source system นั้นเลย ผมอ่านได้แค่สิ่งที่มัน ปล่อยออกมา เท่านั้น
เพราะงั้นงานนี้ไม่เคยเป็นงานเว็บเลยครับ มันคืองาน integration ที่ใส่ชุดเว็บมาหลอกเรา
ข้อจำกัดที่เราไม่ได้เป็นเจ้าของ
ก่อนผมจะเขียนโค้ดสักบรรทัด รูปร่างจริงของโปรเจกต์ถูกกำหนดโดยสิ่งที่ผมคุมไม่ได้เลย:
- ข้อมูลอยู่ on-prem — มันอยู่บนเครื่องที่ออฟฟิศลูกค้า ระบบของผมเลยต้องไปรันที่นั่น ด้วย ไม่ใช่บน cloud สวยๆ ที่ผมจัดการเองได้
- source ไม่มี API — Visual FoxPro ไม่ได้ยื่น endpoint สวยๆ ให้ สัญญาเดียวที่ผมได้ คือไฟล์ที่ script ตัวนั้นปล่อยออกมา
- ข้อมูลที่ลูกค้าให้ไม่เคยครบ — เขาบอกว่าข้อมูลรีเฟรชทุก 1 ชั่วโมง จริงๆ คือ 2 ชั่วโมง รายละเอียดเล็กๆ แต่บทเรียนใหญ่ เดี๋ยวผมจะวนกลับมาเล่า
เมื่อเราเปลี่ยน source ไม่ได้ ข้อจำกัดพวกนี้ คือ ตัว design เลยครับ หน้าที่ผมคือ ออกแบบรอบมัน ไม่ใช่หวังให้มันหายไป
Architecture v1
สุดท้ายมันออกมาแบบนี้ครับ ทุกอย่างด้านล่างรันอยู่บนคอมที่ออฟฟิศลูกค้า ผ่าน Docker และออกสู่โลกภายนอกผ่าน tunnel เส้นเดียว:
คอมที่ออฟฟิศลูกค้า (Windows + Docker)
┌────────────────────────────────────────────────┐
│ Visual FoxPro ──script──▶ CSV files │
│ │ │
│ file watcher │
│ │ │
│ ETL ──▶ database │
│ │ │
│ API server │
└────────────────────────────────────────┬───────┘
│ Cloudflare Tunnel
▼
Next.js (server-side)
│
▼
ผู้เข้าชมเว็บ
แต่ละชิ้นมีอยู่เพราะข้อจำกัด ไม่ใช่เพราะว่ามันน่าสนใจที่จะใส่เข้ามา:
- file watcher → ETL → database เพราะผมอ่านได้แค่ไฟล์ ผมเลยคอย watch ไฟล์ใหม่ที่ ถูกปล่อยมา แปลงข้อมูล แล้วเก็บเป็นก๊อปปี้ของผมเองที่ query ได้
- API server บนเครื่องลูกค้า เพราะข้อมูลมันอยู่ตรงนั้น บวกกับการ์ดมาตรฐานที่ฝั่ง API: API key, CORS, rate limit
- Cloudflare Tunnel เพื่อพา API ที่อยู่ on-prem ออกอินเทอร์เน็ตให้เว็บเข้าถึงได้ ส่วนนี้ ตั้งค่าง่ายจนน่าตกใจ — เป็นชิ้นที่ราบรื่นที่สุดของทั้ง stack เลยครับ
- Next.js สำหรับหน้าบ้าน เพราะผมต้องการให้ส่วนที่เป็นข้อมูล sensitive อยู่ฝั่ง server-side ไม่ให้มันรั่วผ่าน client-side API call
ระหว่างที่คิดเรื่องพวกนี้ ผมใช้ AI เป็นคู่คิดในการออกแบบไปด้วย — ไม่ใช่แค่ให้มันเขียนโค้ด แต่ให้ช่วยเถียง trade-off ออกมาดังๆ และผมยังเก็บ Architecture Decision Record (ADR) ไว้ใน repo ที่เขียนโค้ดเลย เพื่อให้ เหตุผล เบื้องหลังแต่ละการตัดสินใจอยู่ติดกับโค้ด ของแถมที่ไม่ได้คาดไว้คือ มันกลายเป็น context ที่ AI agent กลับมาอ่านย้อนได้ — เดี๋ยวจะ เล่าวิธีนี้ลึกๆ ในโพสต์ถัดไปครับ
ตอนที่โลกแห่งความจริงโต้กลับ
มีสองช่วงที่ผมอยากเก็บไว้เล่า
“ทุก 1 ชั่วโมง” ที่จริงคือทุก 2 ชั่วโมง เพราะผมทำ refresh interval เป็นค่า config มาตั้งแต่วันแรก ไม่ได้ฝังเป็นตัวเลขไว้ลึกๆ ในโค้ด เรื่องนี้เลยเป็นการแก้บรรทัดเดียว ไม่ใช่ การไล่หา นี่แหละบทเรียนทั้งหมด: สมมติฐานของลูกค้าจะผิด เพราะงั้นทำทุกสมมติฐานให้เป็นตัวแปร
การเปิด Docker บน Windows ต้องไปแก้ BIOS ผมพยายามอย่างหนักให้งานนี้เป็นงาน remote ไม่ต้อง onsite แต่ virtualization ถูกปิดไว้ที่ firmware แผนนั้นเลยพังและผมต้องหาวิธีการใหม่
บทเรียนเดียวที่อยากให้จำ
ก่อนเขียนโค้ดบรรทัดแรก ให้ลิสต์ทุกอย่างที่เรา ไม่ได้ เป็นเจ้าของ — source system, ที่อยู่ของข้อมูล, สิ่งที่ลูกค้ายังไม่ได้บอก ลิสต์นั้นแหละคือสิ่งที่กำหนด architecture จริงๆ ที่เหลือเป็นแค่ความชอบส่วนตัว
ตอนหน้า
ไฟล์ที่ออกมาจาก FoxPro กลายเป็น “สัญญา” ที่ไม่มีเอกสารเลยครับ มันครอบข้อความด้วย "
แต่ไม่ escape เครื่องหมาย inch-mark ที่อยู่ข้างใน — CSV parser ปกติเลยพังเงียบๆ
การ reverse-engineer ฟอร์แมตนี้คือเรื่องของโพสต์ถัดไป