← All articles

6 min read Read in English

7 ข้อผิดพลาดตอนออกแบบระบบแจ้งเตือน (ที่ผมเคยทำเองครบทุกข้อ)

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

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

1. ส่งทุกอย่างที่เกิดขึ้น

อาการคือ backend มี event อะไร ก็ยิงแจ้งเตือนหมด ออเดอร์เข้า ออเดอร์อัปเดต ออเดอร์จ่ายเงิน ออเดอร์แพ็ค ออเดอร์ส่ง ลูกค้าหนึ่งคนได้ 5 ข้อความต่อออเดอร์ พนักงานได้ 5 × จำนวนออเดอร์ต่อวัน

ผลที่ตามมาไม่ใช่ "รำคาญ" อย่างเดียว แต่คือคนรับปิดเสียงแล้วพลาดอันที่ต้องทำจริง ๆ อันเดียวในร้อยอัน

คำถามที่ควรถามก่อนส่งทุกครั้ง: ถ้าคนรับไม่เห็นข้อความนี้ จะเกิดอะไรขึ้น? ถ้าคำตอบคือ "ไม่มีอะไร" อย่าส่ง ให้ไปอยู่ในหน้า log หรือ dashboard แทน

2. ไม่มีระดับความสำคัญ

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

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

3. ส่งซ้ำตอนระบบมีปัญหา

อันนี้เจ็บที่สุด เพราะมันเกิดตอนที่คุณกำลังยุ่งที่สุด

ตัวอย่างจริง: worker ส่งข้อความหาลูกค้า 2,000 คน ส่งไปได้ 800 คน แล้ว deploy โค้ดใหม่ worker restart กลับมาเริ่มใหม่ตั้งแต่คนแรก ลูกค้า 800 คนได้ข้อความซ้ำ อีกแบบคือ retry ที่ไม่มีเพดาน เน็ตสะดุดหนึ่งครั้ง ส่งซ้ำไปสิบรอบ

ทางแก้มีสองส่วนและต้องทำทั้งคู่: จำว่าส่งให้ใครไปแล้ว ในระดับผู้รับ ไม่ใช่ระดับงาน และ retry ต้องมีเพดานและเว้นระยะ (exponential backoff) เรื่องนี้ยาวพอจะเป็นบทความแยก: ทำไมการส่งแจ้งเตือนถึงต้องมีคิว

4. ข้อความไม่บอกว่าต้องทำอะไร

"มีออเดอร์ใหม่" — แล้วไง? ต้องเปิดแอปไหน ไปหน้าไหน กดอะไร

ข้อความที่ดีตอบสามอย่างในตัวเอง: เกิดอะไร (ออเดอร์ #1042 จากคุณสมชาย 450 บาท) ต้องทำอะไร (ยืนยันภายใน 15 นาที) และทำได้ตรงไหน (ปุ่ม "รับออเดอร์" ที่กดแล้วจบเลย) ถ้าช่องทางรองรับปุ่ม ใช้ปุ่ม ถ้าไม่รองรับ อย่างน้อยใส่ลิงก์ที่พาไปหน้าที่ถูกต้องเลย ไม่ใช่หน้าแรกของระบบ

5. ส่งตอนตีสาม

ระบบไม่รู้ว่าคนรับหลับอยู่ cron ที่รันเที่ยงคืนส่งสรุปยอดตอน 00:05 รายงานประจำวันที่ควรอ่านตอนเช้ามาถึงตอนคนรับกำลังจะนอน

แยกให้ชัดว่าอะไรต้องปลุก (ระบบล่ม เงินหาย) กับอะไรรอเช้าได้ (สรุปยอด รีวิวใหม่) อย่างหลังให้เก็บไว้แล้วส่งรวมช่วงเวลาที่กำหนด เช่น 08:30 ผู้รับจะขอบคุณคุณมาก และจะไม่ปิดเสียงแอป

6. มีช่องทางเดียว ไม่มีแผนสำรอง

ส่งเข้า LINE อย่างเดียว วันที่ LINE มีปัญหา หรือโควตาหมด หรือลูกค้าบล็อก OA ทุกอย่างเงียบ และคุณไม่รู้ว่าเงียบ

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

7. ไม่เก็บว่าส่งอะไรไปเมื่อไร

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

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

ที่ผมเลือกทำใน Ting

เขียนตามตรงว่าข้อ 3, 6, 7 คือของที่ผมไม่อยากแก้ซ้ำอีกในทุกโปรเจกต์ เลยทำให้เป็นของกลางใน Ting: คิวที่จำผู้รับรายคน retry มีเพดาน ผลการส่งต่อช่องทางดูได้ใน console และ log เก็บเนื้อหาจริงที่ส่ง

ส่วนข้อ 1, 2, 4, 5 ไม่มีเครื่องมือไหนแก้ให้ได้ มันคือการตัดสินใจของคนออกแบบระบบว่าอะไรควรส่งและอะไรไม่ควร ซึ่งเป็นส่วนที่ยากกว่าโค้ดเสมอ