จุดสิ้นสุดของ Excel
เดือนธันวาคมที่ผ่านมา ผมรับช่วงต่อ ERP ที่ทำไว้ครึ่งทาง ฝั่งการขายเขียนด้วย ASP.NET + C# + MS SQL ส่วนฝั่งการผลิตอยู่ในแผนแต่ยังไม่ได้เขียน ผมย้ายมันขึ้น Laravel stack ที่ทันสมัย แล้วถักสายการผลิตขึ้นใหม่จากศูนย์ ทำมาสี่เดือนครึ่งแล้ว ยังไม่จบ สิ่งที่หน้างานสอนผมอยู่ตรงนี้
ผมเขียนเครื่องมือภายในให้ธุรกิจการผลิตและงานปฏิบัติการ โปรเจกต์ที่ผมทุ่มเวลาให้มากที่สุดคือแพลตฟอร์ม ERP/MES ของโรงงานอุตสาหกรรมที่ผมทำงานอยู่ บทความนี้ไม่ใช่บันทึกของระบบที่ผมสร้างเองตั้งแต่ต้น แต่เป็นบันทึกของระบบที่ตกมาถึงมือผมในสภาพทำไว้ครึ่งทาง ผมย้ายมัน ถักเพิ่มลงไป และยังถักอยู่จนถึงตอนนี้ สี่เดือนครึ่งที่สะสมมา
ชีวิตเก่า: Excel + WhatsApp + กระดาษ
ก่อนระบบจะเข้ามา ในโรงงานมีเครื่องมือสามอย่างทำงานคู่ขนานกัน ทีมขายเก็บคำสั่งซื้อและรายงานไว้ในไฟล์ Excel ส่วน แบบฟอร์มกระดาษ ใช้สำหรับแจ้งคำสั่งซื้อ รายงานการผลิต และใบส่งของ ทีมผลิตกรอกรายงานการผลิต ทีมจัดส่งกรอกใบส่งของ ส่วนการสื่อสารระหว่างแผนกไหลอยู่ในกลุ่ม WhatsApp
โครงสร้างแบบนี้มองจากข้างนอกเหมือนงานทำมือ แต่ข้างในมีรอยร้าวใหญ่สองรอย
รอยแรกคือการถูกบังคับให้ เขียนข้อมูลเดียวกันลงสองที่ เวลาฝ่ายขายเปิดคำสั่งซื้อ ข้อมูลต้องถูกคัดลอกไปฝั่งจัดส่งแบบเดียวกัน ในฝ่ายผลิตเองข้อมูลชุดเดียวกันก็กระจายอยู่ในหลายบันทึก สองคนเปิดไฟล์เดียวกันพร้อมกันแล้วคนหนึ่งทับงานอีกคน เป็นเหตุการณ์ปกติ
รอยที่สองหนักกว่า แผ่นไม้ออกจากการผลิตแล้ว ไปตัดขนาด จากนั้นไปโป๊ว ไปขัด แล้วจบที่การอัดฟิล์ม เป็นสายโซ่ห้าห่วง เวลาถามว่า “ตอนนี้ชิ้นนั้นอยู่ที่เครื่องไหน” ระบบเก่าไม่มีใครตอบได้ ผู้จัดการฝ่ายผลิตหรือเปล่า คนตัดขนาดหรือเปล่า ช่างอัดฟิล์มหรือเปล่า ไม่มีใครเลย เพราะไม่มีการบันทึกที่เป็นระบบและมีคนรับผิดชอบ ไม่คุณก็ต้องจำเอาเอง ไม่อย่างนั้นก็ต้องลุกไปดูด้วยตัวเอง
บนแผนที่ข้อมูลที่กระจัดกระจายขนาดนี้ เวลาลูกค้าโทรมา การจะตอบว่า “ได้ครับ ของคุณตอนนี้อยู่ที่เครื่องขัด” เป็นไปไม่ได้เลย
ระบบที่ผมรับช่วงมา: สเปกที่ครอบคลุมแค่ครึ่งเดียว
ธันวาคม 2025 ผมรับมอบแพ็กเกจหนึ่งมา ข้างในมี ERP ที่เขียนด้วย ASP.NET Core 9 + C# + MS SQL Server 2016 เอกสารสเปกฟีเจอร์เจ็ดสิบสี่หน้า และงานออกแบบหน้าจอ ขอบเขตของระบบนิยามไว้สะอาด คือ การจัดการลูกค้า สินค้า (การตั้งราคาแบบพารามิเตอร์และสินค้าที่กรอกเอง) วงจรชีวิตของใบเสนอราคา ใบสั่งงาน multi-currency (ดึงอัตราแลกเปลี่ยนรายวันจาก TCMB API) multi-company (แยกข้อมูลตามบริษัทเจ้าของ) บทบาทและสิทธิ์ แดชบอร์ด และการตั้งค่าแพลตฟอร์ม
พูดอีกอย่างคือ ฝั่งการขาย เสร็จแล้ว ตั้งแต่รับคำขอจากลูกค้า ตั้งราคาสินค้าตามพารามิเตอร์ ทำเวอร์ชันของใบเสนอราคา ไปจนถึงแปลงเป็นใบสั่งงานเมื่ออนุมัติ ส่วนนี้เขียนไว้หมดแล้ว
ประโยคในสเปกเองก็พูดชัด
“this proposal system is intended as the very first part of a much larger ERP system that is being planned. It can be taught as the first module of a complete ERP system, which can be used to manage the entire plywood factory’s production and other operations.”
แปลว่า อยู่ในแผนแต่ยังไม่ได้เขียน ส่วนที่ไปจนถึงการออกใบสั่งงานเป็นระบบแล้ว หลังจากนั้น คือการผลิต การตัดขนาด การจัดส่ง สต็อก การตามล็อต ป้ายฉลาก ที่หน้างานยังเป็น Excel + WhatsApp + กระดาษอยู่เหมือนเดิม
การตัดสินใจก่อตัวขึ้นตรงนี้
การตัดสินใจย้าย: ทำไมต้อง Laravel + PHP + MySQL
จะอยู่กับ stack C# เดิมแล้วเพิ่มโมดูลใหม่เข้าไปก็ทำได้ แต่การตัดสินใจง่ายขึ้นเพราะมีต้นทุนสามรายการหายไปพร้อมกัน
- ไม่มีค่าไลเซนส์ ไลเซนส์ MS SQL Server และ Windows Server เป็นบรรทัดงบประมาณถาวร ส่วน PHP, MySQL, Linux ฟรี
- เซิร์ฟเวอร์ตั้งอยู่แล้ว ผมมีเซิร์ฟเวอร์ของตัวเองบน Linux อยู่แล้ว มี Caddy ทำ reverse proxy มี Tailscale ทำเครือข่ายส่วนตัว มี Docker แยกเซอร์วิส ไม่ต้องไปตั้งเซิร์ฟเวอร์ Windows เพิ่ม
- คนในบรรทัดค่าพัฒนาคือผมเอง ถ้าจ้างทีมซอฟต์แวร์ ทุกคำขอฟีเจอร์เท่ากับค่าแรงต่อชั่วโมง ถ้าผมทำเอง บรรทัดนั้นก็หลุดออกจากงบ เพราะคนในบรรทัดนั้นคือผม
พอสามรายการหายพร้อมกัน การเลือกย้ายจาก ASP.NET Core 9 ไป Laravel 12 + PHP 8.3 + MySQL ก็สมเหตุสมผล ฝั่ง frontend เป็น HTML 5 + CSS 3 + JavaScript อยู่แล้ว ตรงนั้น stack ไม่เปลี่ยน ผมเขียนของเทียบเท่าขึ้นใหม่ด้วย Blade template engine + Alpine.js + TailwindCSS
สองสามเดือนแรกแทบทั้งหมดเป็นงาน port คืออ่านโค้ด C# ทีละบรรทัดแล้วเขียนโค้ด PHP/Laravel ที่ทำงานเดียวกัน ลูกค้า สินค้า การตั้งราคาแบบพารามิเตอร์ วงจรใบเสนอราคา ใบสั่งงาน การแยกข้อมูลแบบ multi-tenant หลายสกุลเงิน การทำเวอร์ชันแบบ snapshot ทั้งหมดอยู่ในสเปก ผมย้ายมาทีละอย่างตามลำดับ
ผมถักอะไรเพิ่มขึ้นไปบ้าง
ส่วนที่น่าสนใจจริงเริ่มหลังการย้าย คือการเพิ่มสายการผลิตที่ไม่ได้เขียนไว้ในสเปก
โมดูลแรกคือ การวางแผนการผลิต ผมเริ่มจากตรงนั้นโดยตั้งใจ เพราะมันเป็นต้นสายโซ่ ขั้นตอนถัดไปทั้งหมด (ตัดขนาด โป๊ว ขัด อัดฟิล์ม) จะกินอาหารจากการผลิต จากนั้นผมก็ถักสายหน้างานทีละห่วง
- ตัดขนาด การจ่ายงานเข้าเครื่อง การวางแผนวันทำงาน การตามเข้าออก
- โป๊ว คิวงาน การบันทึกงานเสร็จ การตามของเสีย
- ขัด คิวสถานะ การคำนวณปริมาตร (ลูกบาศก์เมตร)
- อัดฟิล์ม การนิยามเครื่องอัด การตั้งค่าช่อง การวางแผนแผ่นฟิล์ม บันทึกรายวัน
รอบ ๆ สิ่งเหล่านี้มีระบบ ตามล็อต เข้ามา แต่ละชิ้นมีเลขไม่ซ้ำ อยู่ขั้นตอนไหน ใครรับผิดชอบ ผ่านเมื่อไหร่ ผมเพิ่มการเชื่อมต่อ ฉลาก + QR + เครื่องพิมพ์ TSPL ลงไปบนนั้น ตอนนี้แผ่นไม้ถูกติดฉลากตั้งแต่ออกจากการผลิต แล้วสแกนได้ตลอดทั้งหน้างาน จากนั้นก็มี สต็อก การจัดส่ง เอกสารแนบสำหรับส่งออก (ATR, EUR.1, ใบแจ้งหนี้, ใบส่งของ) สถานะคำสั่งซื้อเฉพาะของแต่ละ tenant และ pessimistic locking ในการอัปเดตการผลิต เพื่อไม่ให้เกิด race condition เวลาสองคนเขียนลงแถวเดียวกัน
วิธีทำงานของผมไม่ใช่วินัยแบบ sprint ผม เขียนสถานการณ์เป็นวัน ๆ ไป ว่า “ในฉากนี้พนักงานทำอะไร ระบบบันทึกอะไร การคำนวณตัวไหนทำงาน” แล้วลงหน้างาน ให้ทีมได้ใช้จริง เก็บเสียงตอบกลับ แก้ข้อผิดพลาด แล้วส่งกลับไปใหม่ เพราะแบบนี้จึงไม่มีการ “ปิดโมดูลแล้ววางมือ” ตามลำดับ ผมกลับไปแก้โมดูลเดิมอยู่เรื่อย ๆ
สองบทเรียนที่หน้างานสอนผม
มีสองบทเรียนที่ถ้าขาดไป ต่อให้แพลตฟอร์มราคาเป็นล้านก็ลงถังขยะ
”ถ้าข้อมูลแรกที่กรอกผิด ทั้งสายก็เน่า”
ระบบจะสง่างามแค่ไหนก็ตาม ถ้า ข้อมูลดิบถูกกรอกผิด รายงานที่ปลายสายก็ผิดตาม รูปที่ชัดที่สุดของเรื่องนี้โผล่มาตรงนี้ ล็อตที่บันทึกตอนออกจากการผลิตว่า 100 ชิ้น พอมาถึงการตัดขนาดกลับยืนยันได้ 95 ชิ้น ห้าชิ้นที่หายไปกลายเป็นผี เดินอยู่ในระบบ มีอยู่ในรายงาน แต่ไม่มีอยู่ที่หน้างาน
การรัดกุมเพื่อรับมือเรื่องนี้คือสิ่งที่ผมทำอยู่ตอนนี้ ต้องสร้างแบบจำลองของ ตัวแปร ไว้ล่วงหน้าแล้วบังคับตั้งแต่ขั้นตอนกรอกข้อมูลดิบ ใครกรอก กรอกเมื่อไหร่ ภายใต้เงื่อนไขอะไร อ้างอิงจากอะไร ตอนออกแบบระบบผมไม่ได้เห็นเรื่องพวกนี้ ผมเรียนมันจากการกลับไปกลับมาที่หน้างาน
GIGO เป็นเรื่องคลาสสิก garbage in, garbage out เป็นประโยคในตำรา เวอร์ชันที่หน้างานแข็งกว่านั้นมาก ซอฟต์แวร์เก็บเงินคุณจากครึ่งหลังของประโยค ส่วนครึ่งแรกคนที่หน้างานเป็นคนเก็บเงินคุณ
”การโน้มน้าวผู้ใช้ไม่ใช่คำถามทางเทคนิค”
ระบบพร้อม หน้าจอทำงาน ปุ่มอยู่ครบ พนักงานก็ยังอยากกรอกลง Excel อยู่ดี เพราะ Excel คือไฟล์ที่เขาเก็บมาหลายปี การเปิดมันเป็นรีเฟล็กซ์ ส่วนระบบของคุณใหม่ แปลกหน้า และเป็นผังหน้าจอที่เขายังไม่เข้าใจ
ถ้าทีมหน้างาน ไม่เต็มใจและไม่รู้สึกรับผิดชอบ ระบบก็เน่าอยู่ดี ซอฟต์แวร์อาจจะดี การอบรมอาจจะจัดไปแล้ว แต่ถ้ามีสักหนึ่งสองคนบอกว่า “ผมจะเก็บใน Excel ต่อไป” สายโซ่ก็ขาด เพราะพอห่วงเดียวในสายหยุดบันทึกข้อมูล รายงานย้อนหลังก็ไม่ซื่อสัตย์อีกต่อไป
นี่ไม่ใช่ปัญหาที่แก้ได้ด้วยการเขียนโค้ด ส่วนหนึ่งแก้ได้ด้วยการปรับ UX ให้ใช้ง่ายกว่า Excel ที่เหลือขึ้นอยู่กับการตัดสินใจของทีมและท่าทีของผู้บริหาร
การเป็นทั้งผู้พัฒนาและผู้ใช้ปลายทางในคนเดียวกัน
ไม่ใช่ส่วนผสมที่พบบ่อยนัก ผมเขียน ERP อยู่ และในเวลาเดียวกันก็ใช้มันในฐานะผู้รับผิดชอบงานขายและงานจัดส่ง
ข้อได้เปรียบชัดเจน เวลาอ่านสเปกผมไม่ได้คิดว่า “ผู้รับผิดชอบงานขายจะทำตัวอย่างไร” แต่คิดว่า “ในฐานะผู้รับผิดชอบงานขาย ตอนนี้ผมต้องการอะไร” ปีนี้เป็นปีที่ 11 ของผมในวงการนี้ ใบเสนอราคาเปิดกันอย่างไร ตู้คอนเทนเนอร์ออกจากตุรกีไปทางไหนถึงที่ไหน ค่าระวางคิดกันอย่างไร ลูกค้าถามอะไรทางโทรศัพท์ ภาพหน้าจอมันประกอบอยู่ในหัวผมมาก่อนแล้ว นี่คือบริบทที่นักพัฒนาจากข้างนอกไม่มีทางมีได้
แต่มันมีราคาที่ต้องจ่าย และต้องพูดกันตรง ๆ
ความเสี่ยงข้อแรก คืองานหลักของผมไม่ใช่งานนี้ งานขายและงานจัดส่งคืองานหลัก ซอฟต์แวร์เป็นงานรอง เพราะแบบนี้จึงไม่มีวินัยแบบ sprint เบื่อเมื่อไหร่ก็เติมไปชิ้นหนึ่ง อีกวันก็ไม่แตะเลย ผมวางแผนว่า “ฟีเจอร์สำคัญตัวนี้จะใช้เวลาสองสัปดาห์” ไม่ได้ เพราะวันทำงานสองสัปดาห์นั้นถูกจองไว้ให้งานขายอยู่แล้ว
ความเสี่ยงข้อสอง คือการไม่มีความเห็นที่สวนทาง เวลาคุณใช้ของที่คุณเขียนเอง ความคิดที่ว่า “ถ้าเป็นแบบนี้น่าจะดีกว่า” มันปิดตัวเองจากคำถามของนักพัฒนาข้างนอกที่ว่า “คนใช้ของนี้คือใคร เขาคิดแบบนี้จริงหรือเปล่า” การตัดสินใจทางซอฟต์แวร์ของผมบางส่วนอาจจะมั่วก็ได้ เพราะผมทำงานแบบไม่มีวินัย และเพราะคนที่รู้เรื่องนี้มีผมคนเดียว จึงไม่มีสายตาจากข้างนอกมาบอก
บทความนี้ก็เป็นบันทึกกันตาบอดแบบนั้นอยู่บ้างเหมือนกัน
ปิดท้าย
ถ้าความสมบูรณ์ของข้อมูลไม่มี ต่อให้คุณออกแบบสง่างามแค่ไหน ซอฟต์แวร์ “ราคาเป็นล้าน” นั้นก็ลงถังขยะ ถ้าทีมหน้างานไม่รู้สึกรับผิดชอบ ระบบก็เน่าอยู่ดี สองประโยคนี้คือสิ่งที่สี่เดือนครึ่งสอนผมแรงที่สุดเมื่อมองย้อนกลับไป
ไม่มีคำว่า “จบแล้ว” อาจจะเป็นแบบนี้ต่อไปอีกปีหนึ่ง หรืออาจจะสามปี การถักระบบขึ้นมา การทดสอบที่หน้างาน การเปลี่ยนความเคยชินของผู้ใช้ การทำให้วินัยเรื่อง “ข้อมูลแรกที่กรอก” ลงหลักปักฐาน ไม่มีอันไหนเลยที่เป็นงานซึ่งมีวันปล่อยตัว คุณเขียนซอฟต์แวร์เสร็จ เสียงตอบกลับจากหน้างานมา คุณกลับไปแก้ แล้วก็วนอีก
บทเรียนหลักอาจจะเป็นข้อนี้ การเขียนโรงงานขึ้นมาไม่ใช่งานที่ทำครั้งเดียวจบ มันคือความสัมพันธ์ที่เติบโตไปด้วยกัน ฝังอยู่ที่หน้างาน และอยู่กันยาว ระบบคลังสินค้าอิสระที่ผมถักไว้ข้าง ๆ ระบบนี้ ผมเล่าไว้ในบทความ บันทึกของไม้ท่อนหนึ่ง