← กลับไปหน้าบทความ
คู่มือ

แก้โค้ด CAPI ตอนเที่ยงคืน เช้าวันรุ่งขึ้น Currency หายทั้ง Batch เพราะไม่มีใครทดสอบก่อน Merge วาง Contract Test เข้า CI ให้จับได้ก่อนขึ้นจริง

ทีมบรรณาธิการ linli07 ก.ย. 07:43อัปเดต 07 ก.ย. 07:43อ่าน 3 นาที
แก้โค้ด CAPI ตอนเที่ยงคืน เช้าวันรุ่งขึ้น Currency หายทั้ง Batch เพราะไม่มีใครทดสอบก่อน Merge วาง Contract Test เข้า CI ให้จับได้ก่อนขึ้นจริง
บทความนี้จัดทำเพื่อความรู้ ชื่อธุรกิจ ตัวเลข หรือบทสนทนาที่ไม่มีแหล่งอ้างอิงกำกับเป็นกรณีตัวอย่างเพื่ออธิบายแนวคิด ดูรายละเอียดที่ นโยบายกองบรรณาธิการ

สรุปสั้น ๆ

การทดสอบด้วยมือผ่าน test event code ช่วยจับบั๊กได้เมื่อคนทำจำได้ว่าต้องทดสอบ แต่เมื่อทีมโตขึ้นและมีคนแก้โค้ดหลายคนพร้อมกัน จุดที่มองข้ามคือไม่มีใครบังคับให้ทดสอบก่อน merge ทุกครั้ง การวาง contract test อัตโนมัติเข้า CI pipeline ที่ตรวจ schema ของ payload ทุกครั้งที่มีการเปลี่ยนโค้ด คือทางที่ทำให้ความผิดพลาดถูกจับได้ก่อนขึ้นจริงเสมอ ไม่ต้องพึ่งความจำของคน

ทีมพัฒนาระบบ CAPI ของธุรกิจหนึ่งที่ผมช่วยรีวิว เคยมีเหตุการณ์ที่นักพัฒนาคนหนึ่งแก้โค้ดตอนดึกเพื่อเพิ่มฟิลด์ใหม่ในการยิง event แล้ว refactor ฟังก์ชันแปลงข้อมูล currency ไปพร้อมกันด้วยความรีบ merge เข้า main branch แล้วเช้าวันรุ่งขึ้นก็ deploy ขึ้น production ตามรอบปกติของทีม โดยไม่มีใครทดสอบผ่าน test event code ก่อนเลยเพราะคิดว่าการแก้ไขเล็กน้อยไม่น่ามีปัญหาอะไร

ผลคือ event ทุกตัวที่ยิงออกไปหลัง deploy มีค่า currency เป็นค่าว่างเปล่า เพราะ refactor ที่ทำไปพลาดจุดหนึ่งที่ดึงค่าจากตัวแปรผิดชื่อ ปัญหานี้ไม่มีใครรู้จนกระทั่งทีมการตลาดสังเกตว่า Events Manager แสดง error rate สูงผิดปกติในช่วงบ่ายของวันนั้น กว่าจะไล่หาสาเหตุและแก้ไขได้ก็เสียเวลาไปเกือบครึ่งวัน และ event ที่ยิงผิดไปแล้วหลายพันตัวก็ไม่สามารถย้อนแก้ไขได้

ปัญหานี้ไม่ได้เกิดจากทีมไม่มีความรู้เรื่องการทดสอบเลย พวกเขามีsandbox สำหรับทดสอบ CAPIอยู่แล้ว แต่การทดสอบนั้นเป็นขั้นตอนที่ ‘แล้วแต่คนจะทำหรือไม่ทำ’ ไม่ใช่สิ่งที่ระบบบังคับ บทความนี้จะเจาะการเปลี่ยนการทดสอบจากขั้นตอนที่พึ่งความจำของคน ไปเป็นกลไกอัตโนมัติที่ฝังอยู่ใน CI pipeline

ทำไมการทดสอบด้วยมืออย่างเดียวถึงพังเมื่อทีมโตขึ้น

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

อีกปัจจัยคือความกดดันเรื่องเวลา เมื่อทีมมีงานเร่งด่วนหรือแก้บั๊กตอนดึก การทดสอบด้วยมือที่ต้องเปิด dashboard แล้วรอดู test event มักเป็นขั้นตอนแรกที่ถูกข้ามไปเมื่อรีบ เพราะมันเป็นทางเลือกที่ทำหรือไม่ทำก็ได้ ไม่มีอะไรบังคับ ต่างจากการทดสอบที่ฝังอยู่ใน CI pipeline ซึ่งถ้าไม่ผ่านจะ block การ merge โดยอัตโนมัติไม่ว่าจะรีบแค่ไหนก็ตาม

contract test คืออะไร และตรวจอะไรบ้างที่ต่างจาก unit test ทั่วไป

unit test ทั่วไปมักตรวจว่าฟังก์ชันในโค้ดทำงานถูกต้องตามที่เขียนไว้ แต่ contract test เน้นตรวจว่า payload สุดท้ายที่จะส่งออกไปยัง CAPI ตรงตาม schema ที่แพลตฟอร์มปลายทางกำหนดไว้จริงหรือไม่ เช่น ฟิลด์ที่จำเป็นต้องมีครบ ประเภทข้อมูลถูกต้อง (ตัวเลขไม่ใช่สตริง) รูปแบบ currency ตรงตามมาตรฐาน และค่า hash มีความยาวที่ถูกต้องตามอัลกอริทึมที่ใช้

ความต่างสำคัญคือ contract test ไม่ได้ตรวจแค่ logic ภายในของโค้ดตัวเอง แต่ตรวจ ‘ผลลัพธ์สุดท้ายที่จะสื่อสารกับโลกภายนอก’ ซึ่งเป็นจุดที่ error ร้ายแรงที่สุดมักเกิดขึ้น เพราะต่อให้ logic ภายในถูกต้องตาม unit test ทุกตัว แต่ถ้าขั้นตอนสุดท้ายที่ประกอบ payload มีปัญหา ผลลัพธ์ที่ส่งออกไปก็ยังผิดอยู่ดี contract test จึงเป็นตัวจับความผิดพลาดที่ unit test มองไม่เห็น

ผูก contract test เข้ากับ CI pipeline ให้บล็อกการ merge อัตโนมัติ

  1. เขียนชุด schema ที่กำหนดฟิลด์ที่จำเป็นของแต่ละแพลตฟอร์ม (Meta, Google, TikTok) แยกกัน โดยอ้างอิงจากเอกสารล่าสุดของแต่ละเจ้า
  2. สร้าง test case ที่ป้อนข้อมูล order ตัวอย่างหลายรูปแบบเข้าไปในฟังก์ชันประกอบ payload แล้วตรวจผลลัพธ์กับ schema ที่กำหนดไว้
  3. ตั้งค่าให้ CI pipeline รันชุด contract test นี้ทุกครั้งที่มีการเปิด pull request หรือพยายาม merge เข้า main branch โดยอัตโนมัติ ไม่ต้องรอให้ใครสั่งรันเอง
  4. กำหนดให้การ merge ถูกบล็อกทันทีถ้า contract test ตัวใดตัวหนึ่งไม่ผ่าน ไม่ว่าคนที่แก้โค้ดจะตั้งใจแก้ส่วนนั้นหรือไม่ตั้งใจก็ตาม
  5. เพิ่มtest case สำหรับการยิงซ้ำและกรณี edge case อื่น ๆ เข้าไปในชุดเดียวกัน เพื่อให้ CI ครอบคลุมมากกว่าแค่โครงสร้าง payload อย่างเดียว

contract test ใน CI กับ sandbox ทดสอบมือ ใช้แทนกันได้หรือต้องมีทั้งคู่

จุดเปรียบเทียบContract test ใน CISandbox ทดสอบมือ
เวลาที่ตรวจพบปัญหาก่อน merge เข้า main branch ทุกครั้งอัตโนมัติเฉพาะตอนที่มีคนจำได้และเปิดทดสอบเอง
ครอบคลุมอะไรโครงสร้าง schema และ logic การประกอบ payloadพฤติกรรมจริงเมื่อเชื่อมต่อกับแพลตฟอร์มจริงผ่าน test event
เหมาะกับจับ regression จากการแก้โค้ดที่ไม่ตั้งใจกระทบ payloadตรวจการเชื่อมต่อจริงและ match quality ก่อนขึ้น production ครั้งใหญ่

ดูแล schema ให้ทันสมัยเมื่อแพลตฟอร์มเปลี่ยนแปลงเอกสาร

ความท้าทายของ contract test คือ schema ที่ใช้อ้างอิงต้องอัปเดตตามเอกสารของแพลตฟอร์มโฆษณาที่อาจเปลี่ยนแปลงได้ ถ้าไม่มีใครดูแล schema นี้ให้ทันสมัย มันอาจกลายเป็นสิ่งที่ตรวจสอบข้อมูลตามมาตรฐานเก่าที่ไม่ตรงกับที่แพลตฟอร์มใช้อยู่จริงในปัจจุบัน ซึ่งจะให้ผลลัพธ์การทดสอบที่หลอกลวงว่าผ่านทั้งที่จริงอาจมีปัญหา

แนวทางที่ช่วยได้คือกำหนดให้มีผู้รับผิดชอบตรวจสอบเอกสารของแต่ละแพลตฟอร์มเป็นระยะ และผูกการอัปเดต schema เข้ากับกลยุทธ์ versioning ของ event ที่ไหลผ่านระบบ เพื่อให้การเปลี่ยนแปลงทั้งสองด้าน (schema ที่ทดสอบ และ schema ที่ event จริงใช้) สอดคล้องกันเสมอ ไม่ใช่แยกกันดูแลจนหลุดจากกัน

คำนวณต้นทุนที่เสียไปจริงจากเหตุการณ์ currency หายทั้ง batch

กลับไปที่เหตุการณ์จริงของทีมที่แก้โค้ดตอนดึกแล้ว currency หายทั้ง batch เมื่อลองประเมินต้นทุนที่เกิดขึ้นจริงจะเห็นภาพชัดขึ้นว่าทำไมการลงทุนสร้าง contract test ถึงคุ้มค่า ต้นทุนแรกคือเวลาที่ทีมต้องใช้ไล่หาสาเหตุเกือบครึ่งวัน ต้นทุนที่สองคือ event หลายพันตัวที่ยิงผิดไปแล้วไม่สามารถย้อนแก้ไขได้ ทำให้ข้อมูลของช่วงเวลานั้นเสียหายถาวร และต้นทุนที่สามซึ่งมักถูกมองข้ามคือความเชื่อมั่นของทีมการตลาดต่อคุณภาพข้อมูลที่ backend ส่งให้ ซึ่งเมื่อเกิดเหตุการณ์แบบนี้ขึ้นครั้งหนึ่ง ทีมการตลาดมักเริ่มตั้งคำถามกับตัวเลขทุกครั้งที่เห็นความผิดปกติแม้เพียงเล็กน้อยในอนาคต

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

อีกมุมที่ควรพิจารณาคือ ต้นทุนของการไม่มี contract test ไม่ได้จำกัดอยู่แค่เหตุการณ์ใหญ่ที่เห็นชัดแบบ currency หายทั้ง batch เท่านั้น แต่ยังรวมถึงความผิดพลาดเล็ก ๆ ที่ไม่มีใครสังเกตเห็นเลยเป็นเวลานาน เช่น ฟิลด์หนึ่งที่ส่งค่าผิดรูปแบบไปเรื่อย ๆ โดยที่แพลตฟอร์มปลายทางไม่ได้ปฏิเสธ event แต่แค่รับข้อมูลคุณภาพต่ำเข้าไปเงียบ ๆ ซึ่งความเสียหายประเภทนี้สะสมไปเรื่อย ๆ โดยไม่มีจุดใดที่ทำให้เกิด error ชัดเจนพอจะกระตุ้นให้ทีมสังเกตเห็นและแก้ไข

สร้างวัฒนธรรมทีมที่มองการทดสอบเป็นส่วนหนึ่งของงาน ไม่ใช่ภาระเพิ่ม

การมี contract test ที่ดีในทางเทคนิคยังไม่พอ ถ้าวัฒนธรรมของทีมยังมองว่าการทดสอบเป็นภาระที่ทำให้งานช้าลง นักพัฒนาบางคนอาจพยายามหาทางข้ามการทดสอบเมื่อรีบ เช่น commit โค้ดที่ปิด test ไว้ชั่วคราวแล้วลืมเปิดกลับ หรือเขียน test แบบผิวเผินที่ผ่านได้ง่ายเกินไปโดยไม่ได้ตรวจสอบอะไรจริงจัง การแก้ปัญหานี้ต้องอาศัยการสื่อสารจากหัวหน้าทีมว่าทำไมการทดสอบถึงสำคัญ พร้อมยกตัวอย่างเหตุการณ์จริงที่เคยเกิดขึ้นให้ทีมเห็นภาพชัดเจนว่าต้นทุนของการไม่ทดสอบสูงกว่าเวลาที่เสียไปกับการทดสอบมากแค่ไหน

ทีมที่มีวัฒนธรรมแข็งแรงเรื่องนี้มักปฏิบัติต่อ contract test ที่ล้มเหลวเหมือนเป็นสัญญาณเตือนที่มีค่า ไม่ใช่อุปสรรคที่น่ารำคาญ เมื่อมองแบบนี้ นักพัฒนาจะรู้สึกขอบคุณเมื่อ test จับปัญหาได้ก่อน merge แทนที่จะรู้สึกหงุดหงิดที่ต้องแก้ไขเพิ่มเติม ซึ่งเป็นทัศนคติที่ทำให้ระบบทดสอบทำงานได้อย่างมีประสิทธิภาพในระยะยาว

สรุป

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

จุดเริ่มต้นที่ดีที่สุดไม่ใช่การสร้างระบบทดสอบที่สมบูรณ์แบบตั้งแต่วันแรก แต่คือการเริ่มมี contract test ง่าย ๆ สักสองสามเคสที่ครอบคลุมความผิดพลาดที่เคยเกิดขึ้นจริงในอดีต แล้วค่อย ๆ เพิ่มความครอบคลุมไปตามเวลา

  • การทดสอบด้วยมืออย่างเดียวพังง่ายเมื่อทีมโตขึ้น เพราะเป็นขั้นตอนที่พึ่งความจำของคน ไม่ใช่กลไกบังคับ
  • contract test ตรวจ payload สุดท้ายที่จะส่งออกไปตรงตาม schema ของแพลตฟอร์มปลายทางหรือไม่ ต่างจาก unit test ที่ตรวจแค่ logic ภายใน
  • ผูก contract test เข้า CI pipeline ให้บล็อกการ merge อัตโนมัติเมื่อไม่ผ่าน เพื่อไม่ให้ความผิดพลาดหลุดไปถึง production

คำถามที่พบบ่อย

contract test ต่างจากการตรวจ signature ที่ endpoint รับ webhook อย่างไร

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

ทีมเล็กที่ยังไม่มี CI pipeline เลย ควรเริ่มยังไง

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

contract test ช่วยจับปัญหาเรื่อง match quality ที่ตกต่ำได้ไหม

ช่วยได้ในระดับหนึ่งถ้าตรวจรวมถึงรูปแบบข้อมูลก่อนแฮชด้วย แต่ match quality ที่แท้จริงยังขึ้นกับปัจจัยอื่นที่ทดสอบล่วงหน้าไม่ได้ทั้งหมด เช่น พฤติกรรมจริงของผู้ใช้ปลายทาง จึงยังต้องมี<a href="/blog/capi-emq-diagnostic-workflow-line">diagnostic workflow แยกต่างหาก</a>สำหรับตรวจ match quality โดยเฉพาะ

ต้องเขียน contract test แยกทุกแพลตฟอร์มเลยไหม หรือใช้ชุดเดียวรวมกันได้

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

ถ้า contract test ผ่านหมดแล้ว ยังต้องทดสอบผ่าน test event code อีกไหมก่อนขึ้นจริง

แนะนำให้ยังคงมีขั้นตอนทดสอบผ่าน test event code สำหรับการเปลี่ยนแปลงที่มีความเสี่ยงสูงหรือกระทบวงกว้าง เพราะ contract test ตรวจได้แค่โครงสร้างข้อมูล ไม่ได้ตรวจว่าแพลตฟอร์มปลายทางรับข้อมูลนั้นแล้วประมวลผลถูกต้องจริงหรือไม่

การเพิ่ม contract test ทำให้ deploy ช้าลงมากไหม

การรันชุดทดสอบอัตโนมัติมักใช้เวลาไม่กี่นาที ซึ่งน้อยกว่าเวลาที่เสียไปกับการไล่แก้บั๊กที่หลุดขึ้น production มาก เมื่อเทียบต้นทุนโดยรวมแล้ว การเพิ่มขั้นตอนนี้ช่วยประหยัดเวลาในระยะยาวมากกว่าที่จะทำให้ช้าลง

ทดลองวัดผลโฆษณาเข้า LINE ฟรี 14 วัน

สมัครแล้วสร้างโปรเจกต์ เชื่อม LINE ติดตั้ง Tracking แล้วเริ่มเห็น Funnel จาก Click ถึง Purchase ได้ทันที ไม่ต้องใช้บัตรเครดิต

เริ่มทดลองใช้ฟรี 14 วัน

บทความที่เกี่ยวข้อง

ดีลองค์กรที่ต้องผ่านทั้งฝ่ายจัดซื้อและผู้บริหาร แต่ละคนทักไลน์คนละครั้ง จะออกแบบ attribution สำหรับผู้ตัดสินใจหลายคนยังไง

ดีลองค์กรที่ต้องผ่านทั้งฝ่ายจัดซื้อและผู้บริหาร แต่ละคนทักไลน์คนละครั้ง จะออกแบบ attribution สำหรับผู้ตัดสินใจหลายคนยังไง

ดีล B2B มักไม่ได้ตัดสินใจโดยคนเดียว ฝ่ายจัดซื้อทักถามราคา ฝ่ายเทคนิคทักถามสเปก ผู้บริหารทักถามเงื่อนไขก่อนเซ็นอนุมัติ แต่ละคนเห็นจุดสัมผัสคนละชุด จะรวมเป็นภาพเดียวเพื่อวัด attribution ได้ยังไง
ลูกค้าทิ้งตะกร้าแล้วกลับมาซื้อเพราะข้อความ Broadcast ในไลน์ ไม่ใช่โฆษณา จะนับจุดสัมผัสที่ไม่ได้มาจากแอดใน attribution model ยังไง

ลูกค้าทิ้งตะกร้าแล้วกลับมาซื้อเพราะข้อความ Broadcast ในไลน์ ไม่ใช่โฆษณา จะนับจุดสัมผัสที่ไม่ได้มาจากแอดใน attribution model ยังไง

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

ร้านคนเดียวไม่มีทีมดาต้า จะเริ่มเก็บข้อมูลลูกค้าจาก LINE ยังไงให้ไม่ยุ่งยากเกินไป

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