← Blog

ตั้งรหัส error อย่างไรให้ใช้งานได้จริง — รหัสสองประเภทที่ทำงานคู่กัน

เวลาระบบมีปัญหา ผู้ใช้จะโทรมาบอกว่า “กดแล้วขึ้นแดง ๆ” ซึ่งไล่หาไม่เจอ

ทางแก้ที่ทุกคนคิดออกคือใส่รหัสให้ผู้ใช้คัดลอกมาแจ้ง แต่คำถามที่มักไม่ถูกถามคือ — รหัสนั้นควรตอบคำถามอะไร

เราพบว่ามันมีคำถามสองข้อที่ต่างกันสิ้นเชิง และรหัสเดียวตอบทั้งสองไม่ได้

คำถามสองข้อ

คำถาม ต้องการรหัสแบบไหน
“เหตุการณ์นี้เกิดตอนไหน ไปดู log ครั้งนั้นได้ที่ไหน” รหัสที่ไม่ซ้ำกันทุกครั้ง
“อาการแบบนี้เคยเจอไหม สาเหตุคืออะไร แก้ยังไง” รหัสที่เหมือนเดิมทุกครั้งที่อาการเดียวกัน

สังเกตว่าคุณสมบัติที่ต้องการขัดกันโดยตรง ข้อแรกต้องไม่ซ้ำ ข้อสองต้องซ้ำ

ระบบที่มีรหัสแบบเดียวจึงตอบได้ข้อเดียวเสมอ และมักจบลงที่การมีรหัสที่ไม่ซ้ำ ซึ่งบอกได้แค่ว่า “ไปดูตรงนี้” แต่ไม่ช่วยอะไรเลยเมื่อเป็นปัญหาที่เคยเจอมาแล้วสิบครั้ง

เราจึงมีรหัสสองประเภท

1. รหัสประจำเหตุการณ์

รูปแบบ EE-{ส่วนงาน}-{หน้าจอ}-{รหัสสุ่ม}

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

ส่วน “ส่วนงาน” กับ “หน้าจอ” ที่อยู่ในรหัสมีประโยชน์ก่อนจะเปิด log ด้วยซ้ำ — เห็นรหัสก็รู้แล้วว่าปัญหาอยู่โซนไหนของระบบ

2. รหัสประจำอาการ

รูปแบบ EE-{ชั้น}-{หมวด}-{เลข}

เป็นรหัสที่คงที่ ผูกกับ “อาการ” ไม่ใช่ “ครั้ง” ใช้เมื่อเรารู้จักอาการนั้นดีพอจะบอกสาเหตุและวิธีแก้ได้

รหัสนี้เป็นของเสริม ไม่ใช่ทุก error จะมี เพราะการตั้งรหัสประจำอาการให้กับสิ่งที่ยังไม่เข้าใจก็ไม่มีประโยชน์

ตอนนี้เรามีชุดรหัสประจำอาการอยู่ 4 กลุ่มตามโดเมนงาน — งานติดตั้งและดูแลระบบ · งานเชื่อมมาร์เก็ตเพลส · งาน AI · งานเชื่อมขนส่ง

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

กฎการตั้งเลข — เดาได้และค้นได้

ตัวอย่างที่ชัดที่สุดคือชุดรหัสของงานเชื่อมขนส่ง ซึ่งใช้รูปแบบ EE-ISHIP-{การกระทำ}-{เลข}

จองเลขต้น ๆ ให้ชนิดพื้นฐาน

เลข 001 ถึง 005 จองไว้ให้ความล้มเหลวพื้นฐาน 5 ชนิดเหมือนกันทุกการกระทำ

  • 001 — ยืนยันตัวตนไม่ผ่าน
  • 002 — เชื่อมต่อไม่ได้
  • 003 — ปลายทางมีปัญหา
  • 004 — อ่านคำตอบไม่ออก
  • 005 — เงื่อนไขทางธุรกิจไม่ผ่าน

ผลคือเดาได้ — เห็นเลขลงท้าย 002 ก็รู้ทันทีว่าเป็นปัญหาการเชื่อมต่อ ไม่ว่าจะมาจากการกระทำไหนในทั้ง 15 แบบ และค้นได้ — อยากดูปัญหาการเชื่อมต่อทั้งหมดก็ค้นด้วยลงท้าย 002 ทีเดียว

เลข 999 สงวนไว้สำหรับกรณีที่จัดกลุ่มไม่ได้ ส่วนเลข 010 ขึ้นไปใช้กับอาการเฉพาะที่รู้จักแล้ว

กฎข้อเดียวที่ใช้ตัดสินว่าควรแยกรหัสหรือไม่

แยกรหัสเมื่อ “วิธีแก้” ต่างกัน ไม่ใช่เมื่อ “อาการ” ต่างกัน

ตัวอย่างจริงจากชุดรหัสงานขนส่ง

  • ขอสิทธิ์เข้าใช้ระบบไม่ผ่าน (เลข 001) — โทเคนหมดอายุ วิธีแก้คือขอใหม่ ระบบทำเองได้
  • ขอสิทธิ์ไม่ผ่านเพราะรหัสผ่านผิด (เลข 010) — อาการที่ปลายทางเหมือนกันเป๊ะ แต่วิธีแก้คนละเรื่อง ต้องมีคนไปแก้ค่าตั้งค่า ขอใหม่กี่รอบก็ไม่ผ่าน

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

กฎนี้ทำให้จำนวนรหัสไม่บานปลาย เพราะอาการที่ต่างกันแต่แก้เหมือนกันก็ไม่ต้องแยก

อาการอื่นที่เราแยกรหัสด้วยเหตุผลเดียวกัน

  • ตอบสำเร็จแต่ไม่มีเลขพัสดุกลับมา — ดูเหมือนสำเร็จ แต่ยังไม่มีพัสดุจริง ต้องจัดการต่างจากกรณีล้มเหลว
  • ขอไฟล์ใบปะหน้าแล้วได้หน้าล็อกอินแทน — ถ้าไม่ตรวจแล้วบันทึกไปตรง ๆ จะได้ไฟล์ที่เปิดไม่ออก และไปรู้ตัวตอนจะปะกล่อง

เส้นแบ่งที่สำคัญไม่แพ้กัน — อะไรไม่ควรมีรหัส

กฎของเราคือ

  • ข้อผิดพลาดจริงของระบบ → ต้องมีรหัสเสมอ
  • ผู้ใช้กรอกข้อมูลไม่ครบหรือผิดเงื่อนไข → ข้อความธรรมดา ไม่ต้องมีรหัส

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

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

รหัสต้องมาพร้อมข้อความที่ทำต่อได้

รหัสอย่างเดียวไม่พอ เป้าหมายของเราผูกกันสองข้อ

  1. ทีมงานรู้ว่าพลาดตรงไหน — ทุก error มีรหัสและบันทึกที่มีโครงสร้างส่งเข้าระบบเฝ้าระวัง ค้นย้อนหลังได้
  2. ผู้ใช้ที่ไม่รู้เรื่องเทคนิคเข้าใจและทำต่อได้ — ข้อความภาษาไทยที่บอกว่าเกิดอะไรและควรทำอย่างไรต่อ ไม่มีข้อความทางเทคนิคดิบ ๆ และมีรหัสให้คัดลอกแจ้ง

ข้อสองสำคัญกว่าที่หลายทีมคิด เพราะข้อความผิดพลาดคือสิ่งที่ผู้ใช้เจอในวันที่แย่ที่สุดของเขา

ทำให้เขียนตามได้จริง

ระบบรหัสจะมีค่าก็ต่อเมื่อทุกคนในทีมทำเหมือนกัน เราจึงเขียนเป็นเอกสารกลางที่ขึ้นต้นด้วยตารางสรุปว่า “ตอนนี้กำลังเขียนอะไร ให้ใช้แบบไหน” อ่านแถวเดียวจบ ไม่ต้องอ่านทั้งเอกสาร

และหลายกรณีคำตอบคือ “ไม่ต้องทำอะไร — ระบบใส่รหัสให้เอง” ซึ่งเป็นคำตอบที่ดีที่สุด เพราะสิ่งที่ต้องจำคือสิ่งที่จะถูกลืม

ตัวอย่างที่เราทำแบบนี้คือชุดเชื่อมต่อขนส่ง ที่ใส่รหัสและส่งเข้าระบบเฝ้าระวังให้เองในตัวมัน คนที่เรียกใช้ไม่ต้องจำว่าต้องบันทึกอะไร

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

เราไปรู้ตัวตอนที่ฟีเจอร์หนึ่งล้มเหลวเงียบ ๆ อยู่นาน แล้วต้องกลับไปใส่การบันทึกทีหลัง คราวนี้เลยใส่ตั้งแต่ต้นและใส่ไว้ที่ชั้นเดียว

สรุป

ถ้าจะสรุปเป็นสามข้อ

  1. รหัสประจำเหตุการณ์กับรหัสประจำอาการ ตอบคนละคำถามและต้องมีทั้งคู่
  2. แยกรหัสเมื่อวิธีแก้ต่างกัน ไม่ใช่เมื่ออาการต่างกัน — กฎนี้ทำให้รหัสมีประโยชน์และไม่บานปลาย
  3. สิ่งที่ไม่ใช่บั๊กต้องไม่มีรหัส — ไม่งั้นระบบเฝ้าระวังจะเต็มไปด้วยเรื่องที่ไม่ต้องแก้

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

Freeable

บริหารจัดการข้อมูลในองค์กร ครบในที่เดียว

ดูรายละเอียด →

อ่านต่อ

Freeable

ค่าหนึ่งค่ามี 4 หน้าที่ — มาตรฐานการแทนค่า เก็บ · ส่ง · คำนวณ · แสดง

ปัญหาเรื่องวันที่ไม่ใช่ปัญหาของวันที่ แต่เป็นหนึ่งตัวอย่างของปัญหาตระกูลเดียวกัน — ค่าหนึ่งค่าถูกใช้ทำ 4 หน้าที่ที่ต่างกัน พอรูปแบบของหน้าที่หนึ่งรั่วไปอีกหน้าที่ ตัวเลขก็เพี้ยนเงียบ ๆ

Freeable

เวลาในระบบมีหลายชั้น และมันไม่ตรงกัน — บทเรียนจากบั๊กที่ไล่ 8 วันแล้วสรุปผิดหลายรอบ

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

Freeable

ระบบเฝ้าระวังข้อผิดพลาดที่ใช้งานได้จริง — บทเรียนจากข้อความที่หายวันละ 113 ฉบับ

ติดตั้งระบบเฝ้าระวังแล้วไม่ได้แปลว่าจะเจอปัญหา บทความนี้เล่าเคสที่ error หนึ่งกินไป 88% ของทั้งหมดแต่ไล่ไม่เจอ 9 วันเพราะชื่อของมันเดาสาเหตุผิด และเล่าว่าเราจัดการเสียงรบกวนอย่างไร