How to Debug “Sent but Not Received” From a Product Team View: ดีบักแบบทีมโปรดักต์ให้จบ ตั้งแต่ระบบส่งจนถึง Inbox
ปัญหา “กดส่งแล้ว ระบบขึ้นว่า Sent แต่ผู้รับบอกว่าไม่เคยได้รับ” เป็นหนึ่งในบั๊กที่ทำให้ทีมโปรดักต์ปวดหัวที่สุด เพราะมันกระทบความเชื่อมั่นทันที: ผู้ใช้จะเริ่มไม่แน่ใจว่าระบบคุณเชื่อถือได้หรือไม่ และทีมซัพพอร์ตจะโดนคำถามซ้ำ ๆ แบบ “ช่วยเช็กให้หน่อย” โดยที่คำตอบไม่สามารถเดาเอาได้
มุมมองแบบทีมโปรดักต์คือ: นี่ไม่ใช่แค่เรื่อง “ส่งสำเร็จ” แต่เป็นเรื่องของ เส้นทางการส่ง (delivery pipeline) ตั้งแต่ระบบของคุณ → ผู้ให้บริการส่งอีเมล (ESP/SMTP) → อินเทอร์เน็ต → ผู้ให้บริการปลายทาง (Gmail/Outlook ฯลฯ) → กฎสแปม/ฟิลเตอร์ → โฟลเดอร์ปลายทาง (Inbox/Promotions/Spam/Quarantine) และบางครั้ง “ถูกปฏิเสธ” ก่อนถึงผู้รับด้วยซ้ำ
บทความนี้เป็น playbook ภาษาไทยแบบใช้งานจริง: แยกอาการให้ถูก, ไล่เช็กตามหลักฐาน, สรุป root cause, และสื่อสารกับผู้ใช้แบบมืออาชีพโดยไม่ทำให้สถานการณ์แย่ลง
1) ตั้งกรอบให้ชัดก่อน: “Sent” ของคุณหมายถึงอะไร?
คำว่า “Sent” ในระบบหลายโปรดักต์มีความหมายแค่ “เรารับคำสั่งส่งแล้ว” หรือ “เรา enqueue แล้ว” ไม่ได้แปลว่า “ปลายทางรับไปแล้ว” และยิ่งไม่แปลว่า “ผู้รับเห็นใน Inbox” ดังนั้นสิ่งแรกที่ทีมโปรดักต์ต้องทำคือ นิยามสถานะให้ตรงกัน
- Accepted: ระบบภายในรับงานส่งแล้ว (อาจยังไม่ออกจากระบบ)
- Queued: อยู่ในคิว รอ worker/SMTP ส่งออก
- Delivered to provider: ส่งถึง ESP/SMTP แล้ว และ provider รับคำสั่ง
- Delivered: ปลายทาง (เช่น Gmail/Outlook) รับข้อความแล้ว (ยังอาจไป Spam ได้)
- Bounced/Rejected: ปลายทางปฏิเสธ (อาจเป็น hard/soft bounce)
- Suppressed: ถูกบล็อกโดยระบบคุณ/ESP เพราะเคย bounce/complaint มาก่อน
ถ้าคุณใช้คำว่า “Sent” ครอบทุกอย่าง คุณจะดีบักยากมาก เพราะคนจะเข้าใจว่า “ถึงผู้รับแล้ว” ทั้งที่จริงอาจยังไม่หลุดจากคิวด้วยซ้ำ
2) แยก “อาการ” ให้ถูก: ปัญหานี้มีได้หลายแบบ
ก่อนเปิด log ให้ตั้งคำถาม 5 ข้อ (สั้น ๆ แต่ทรงพลัง) เพื่อจัดหมวดหมู่ปัญหา:
- เกิดกับผู้รับทุกคนหรือบางคน? (ทุกคน = pipeline หรือ config, บางคน = domain/recipient-specific)
- เกิดกับโดเมนไหนเป็นพิเศษ? (เช่น Gmail อย่างเดียว, Outlook อย่างเดียว)
- เป็นอีเมลประเภทไหน? (OTP/Reset/Invite/Newsletter) เพราะ template และ reputation ต่างกัน
- เกิดเฉพาะช่วงเวลาหรือเกิดตลอด? (เป็นช่วง = rate limit/incident/provider delay)
- ผู้ใช้ตรวจ Spam/Promotions แล้วหรือยัง? บางครั้ง “ได้รับ” แต่ “ไม่เห็น”
การตอบคำถามเหล่านี้จะช่วยให้ทีมไม่หลงไปแก้ DNS ทั้งที่จริงเป็น “suppression” หรือ “คิวตันช่วงพีค”
3) เริ่มจากหลักฐานที่จับต้องได้: Event timeline + Message ID
มุมมองแบบทีมโปรดักต์: ทุกอีเมลต้องตามรอยได้ด้วย Message Identifier อย่างน้อยควรมี internal message_id ของระบบคุณ และถ้าใช้ ESP ควรเก็บ provider message_id ด้วย เพราะการดีบักที่ดีคือการสร้าง timeline แบบนี้:
- T0: ผู้ใช้กดส่ง / trigger event เกิดขึ้น
- T1: ระบบคุณสร้าง payload + ใส่คิว (enqueue)
- T2: worker ดึงจากคิว + ส่งไป provider
- T3: provider ตอบรับ (accepted) หรือปฏิเสธ (rejected)
- T4: provider แจ้ง delivered/bounced/complaint (ผ่าน webhook/event)
ถ้าระบบคุณไม่มี T3/T4 คุณจะมองไม่เห็นว่า “ส่งออกไปแล้ว แต่ถูกปฏิเสธ” หรือ “ส่งถึงปลายทางแล้ว แต่เข้าจังค์” ซึ่งเป็นคนละโลกกันและวิธีแก้คนละแบบ
4) เช็กทีละชั้น: Pipeline Debug แบบเป็นขั้นตอน
Step 1: ตรวจว่า trigger เกิดจริงไหม (Product event)
บางครั้ง UI ขึ้นว่า “ส่งแล้ว” แต่จริง ๆ backend ไม่ได้ trigger อีเมล เช่น rule ไม่เข้า, permission ไม่ครบ, หรือ logic ยกเลิกเพราะ cooldown/anti-abuse ให้เช็ก event log ว่ามี record ของการขอส่งอีเมลหรือไม่ พร้อม context: user_id, recipient, template, locale, time
Step 2: ตรวจคิว (Queue) ว่ามี backlog หรือ drop
ถ้าคุณใช้ queue (เช่น Redis/Rabbit/SQS) ให้ดู metrics: จำนวนงานค้าง, เวลารอคิว, error rate ของ worker, และ dead-letter queue อาการคลาสสิก: ช่วง traffic สูง คิวตัน ทำให้ “ส่งช้า” ผู้รับคิดว่าไม่ได้รับ ทั้งที่แค่ delayed
Step 3: ตรวจการเชื่อมต่อกับ provider (SMTP/ESP)
จุดสำคัญคือ SMTP response / provider accept response ถ้า provider ไม่รับตั้งแต่แรก คุณต้องเห็นเหตุผล เช่น rate limited, invalid recipient, policy block ถ้ามี retry logic ให้ดูว่า retry กี่ครั้งและใช้เวลารวมเท่าไร
Step 4: ตรวจ webhook/event callback (delivered/bounce/complaint)
หลายทีมพลาดตรงนี้: provider ส่ง event กลับมา แต่ระบบคุณรับไม่ครบ (signature fail, endpoint down, timeout) ทำให้ในแดชบอร์ดคุณขึ้นว่า “Sent” ตลอด ทั้งที่จริงมี bounce เต็มไปหมด ให้ตรวจ:
- Webhook endpoint uptime/latency
- Signature verification และการ rotate secret
- การ map provider event → internal status
- การจัดการ event ซ้ำ (idempotency)
5) ถ้าส่งออกไปแล้ว แต่ผู้รับไม่เห็น: แยก 3 โลกใหญ่
หลังจากคุณยืนยันว่า provider รับอีเมลแล้ว ปัญหามักจะอยู่ใน 1 ใน 3 โลกนี้:
โลกที่ 1: “Delivered แต่ไปอยู่ Spam/Promotions/Quarantine”
ผู้รับ “ได้รับจริง” แต่ไม่เห็นใน Inbox ซึ่งเกิดบ่อยมาก โดยเฉพาะเมลที่มีลิงก์เยอะ, ข้อความการตลาด, หรือส่งจากโดเมนที่ยังใหม่ แนวทางเช็ก:
- ขอให้ผู้รับค้นหาอีเมลด้วย keyword หรือโดเมนผู้ส่ง (อย่าพึ่งดูแค่ Inbox)
- ตรวจ subject line และ from name ว่าชัดไหม (หลีกเลี่ยงคำที่ดู spammy)
- ตรวจว่ามีลิงก์ติดตาม/รีไดเรกต์หลายชั้นหรือไม่
- ปรับเนื้อหาให้มีสัดส่วน text มากพอ ไม่ใช่รูป/ปุ่มอย่างเดียว
โลกที่ 2: “Rejected/Bounced” (ปลายทางปฏิเสธ)
ถ้า event กลับมาว่า bounce/reject ต้องอ่านเหตุผลให้เป็น:
- Hard bounce: ที่อยู่อีเมลไม่มีจริง/โดเมนผิด → ต้องแก้ข้อมูลผู้รับ
- Soft bounce: กล่องเต็ม/ชั่วคราว/ถูกหน่วง → อาจ retry ได้
- Policy block: ปัญหา reputation, auth, content → ต้องแก้การตั้งค่าและคุณภาพการส่ง
โลกที่ 3: “Suppression” (ระบบไม่ส่งเพราะเคยมีปัญหา)
นี่เป็นกับดักที่ทำให้ทีมซัพพอร์ตสับสน: ผู้ใช้กดส่งหลายครั้ง แต่ระบบไม่ส่งจริง เพราะ address นั้นอยู่ใน suppression list (เคย bounce หรือร้องเรียนสแปม) ให้ตรวจในระบบว่า recipient ถูก suppress หรือ block ไว้หรือไม่ และมีวิธี clear ที่ปลอดภัยไหม
6) สิ่งที่ทีมโปรดักต์ต้องเช็กเสมอ: SPF / DKIM / DMARC (พื้นฐานที่ทำให้ “ถึง”)
ถ้าอีเมลของคุณไม่ผ่านการยืนยันตัวตนของโดเมน โอกาสไป Spam หรือโดน reject จะเพิ่มขึ้นทันที แม้คุณจะส่ง “สำเร็จ” จากฝั่งระบบก็ตาม ในมุมโปรดักต์คุณไม่จำเป็นต้องเป็นผู้เชี่ยวชาญ DNS ทุกคน แต่คุณต้องมี checklist ที่ทีมทำตามได้
- SPF: อนุญาตให้เซิร์ฟเวอร์/ผู้ให้บริการของคุณส่งแทนโดเมนได้หรือไม่
- DKIM: มีลายเซ็นดิจิทัลในหัวอีเมลเพื่อพิสูจน์ว่าไม่ได้ถูกแก้ไขระหว่างทาง
- DMARC: นโยบายว่าถ้า SPF/DKIM ไม่ผ่าน ให้ปลายทางทำอย่างไร และมีรายงานกลับมาไหม
อาการที่มักเจอ: ส่งจากโดเมนใหม่, เปลี่ยน provider แล้ว DNS ไม่อัปเดต, หรือมี SPF หลายบรรทัดจนชนกัน เมื่อเกิด incident ให้ทีมมี “หน้าเอกสารภายใน” ที่บอกว่า record ที่ถูกต้องคืออะไร และตรวจได้จากที่ไหน
7) Debug แบบ Product จริง ๆ: แยก “Transaction” กับ “Marketing” ออกจากกัน
เมลประเภท OTP/Reset/Invite คือ Transactional ซึ่งควรมี deliverability สูงที่สุด แต่หลายระบบใช้โดเมน/พูลส่งเดียวกับ Newsletter หรือเมลโปรโมชัน ทำให้ reputation ตกแล้วลากกันลงทั้งระบบ
แนวทางแบบทีมโปรดักต์:
- แยกโดเมนหรือ subdomain สำหรับ transactional เช่น mail.example.com หรือ notify.example.com
- แยกเส้นทางส่ง หรืออย่างน้อยแยก template + tracking ให้เบาสำหรับ OTP
- ตั้ง priority: OTP ต้องออกจากคิวก่อนเสมอ (ไม่รอ campaign)
- มี SLA/alert เฉพาะ OTP: latency, bounce rate, provider reject
สิ่งนี้ช่วยลดเคส “ลูกค้ารอ OTP ไม่มา” ซึ่งเป็นเคสที่ทำให้ churn ได้เร็วมาก
8) เรื่องที่หลายทีมมองข้าม: Rate limit, Throttling, และการส่งซ้ำ
เมื่อระบบโตขึ้น ปัญหาจะไม่ได้มาจากบั๊กอย่างเดียว แต่มาจากข้อจำกัดของ provider และการป้องกัน abuse เช่น per-domain rate limit, per-recipient throttling หรือการส่งซ้ำจนถูกมองเป็นสแปม
- ถ้ามีการส่ง OTP ถี่เกินไป: ผู้ให้บริการปลายทางอาจเริ่มปฏิเสธ
- ถ้ามี retry แบบกดรัว: ผู้รับอาจได้หลายฉบับ หรือ provider block โดเมนชั่วคราว
- ถ้าใช้ shared IP pool: คนอื่นส่งแย่ ทำให้คุณโดนลากไปด้วย
วิธีคิดแบบโปรดักต์คือ “ออกแบบระบบให้ผู้ใช้ไม่ต้องกดส่งซ้ำ” เช่น มี countdown, มีปุ่ม resend ที่ปลอดภัย, และบอกสถานะที่จริงกับผู้ใช้ (กำลังส่ง/อาจล่าช้า/กรุณาตรวจ spam) เพื่อหยุดพฤติกรรมที่ทำให้ deliverability แย่ลง
9) Playbook สรุป: เช็กลิสต์ 10 ข้อที่ทีมใช้ทุกครั้ง
- 1) มี event trigger หรือไม่ (ใครกดส่ง, template อะไร, เวลาไหน)
- 2) งานเข้า queue แล้วหรือไม่ และคิวมี backlog ไหม
- 3) worker ส่งออกสำเร็จหรือ error (ดู error code/stack)
- 4) provider ตอบรับหรือปฏิเสธ (accepted vs rejected)
- 5) มี delivered/bounce/complaint event กลับมาหรือไม่
- 6) webhook รับ event ได้ครบหรือไม่ (uptime, signature, mapping)
- 7) recipient อยู่ใน suppression/blocked list หรือไม่
- 8) ตรวจ SPF/DKIM/DMARC และโดเมน/ซับโดเมนที่ใช้ส่ง
- 9) ตรวจ content/subject/link และ inbox placement (spam/promotions)
- 10) ดูรูปแบบการเกิด: เฉพาะโดเมน/เฉพาะช่วงเวลา/เฉพาะ template เพื่อหา root cause
เช็กลิสต์นี้ทำให้การสืบหาปัญหากลายเป็นกระบวนการ ไม่ใช่การเดา และช่วยให้ทีมใหม่เข้ามารับช่วงได้เร็ว
10) วิธีสื่อสารกับผู้ใช้: ตรงไปตรงมา แต่ไม่โยนความผิด
ต่อให้คุณดีบักเก่งแค่ไหน ถ้าสื่อสารไม่ดี ผู้ใช้ก็ยังรู้สึกว่า “ระบบไม่น่าเชื่อถือ” แนวทางที่ทีมโปรดักต์ใช้ได้ทันทีคือการตอบแบบ:
- ยืนยันว่าได้รับรายงานแล้ว และกำลังตรวจสอบตามลำดับหลักฐาน
- ขอข้อมูลเท่าที่จำเป็น: เวลาโดยประมาณ, อีเมลผู้รับ, ประเภทอีเมล (OTP/Invite)
- ให้ขั้นตอนชั่วคราวที่ไม่ทำให้ผู้ใช้เสียเวลา: ตรวจ Spam/ค้นหาโดเมน/ลอง resend หลัง X นาที
- ถ้าพบสาเหตุ ให้สรุปแบบภาษาคน ไม่ใช่ภาษาเทคนิคยาว ๆ
- ถ้ายังไม่พบ ให้บอกว่าอยู่ในขั้นไหนของ pipeline และจะอัปเดตเมื่อมีผล
ผู้ใช้ส่วนใหญ่ไม่ได้ต้องการรายละเอียด SMTP ทั้งหมด เขาต้องการความมั่นใจว่า “ทีมคุณควบคุมสถานการณ์ได้” และมีการแก้ที่ป้องกันไม่ให้เกิดซ้ำ
11) ตัวอย่างสถานการณ์ (สั้น ๆ) เพื่อให้เห็นภาพการไล่เช็ก
สถานการณ์ 1: OTP ไม่มาเฉพาะ Gmail ช่วง 20:00–22:00
ทีมโปรดักต์เริ่มจากดู latency คิว พบว่าช่วงพีค queue time เพิ่มจาก 2 วินาทีเป็น 90 วินาที แล้ว provider เริ่ม throttling ทำให้ส่งช้าลงไปอีก ผู้ใช้กด resend หลายครั้งจนเกิด burst ผลคือบางฉบับเข้าช้า บางฉบับถูกจัดเข้าหมวด Promotions/Spam วิธีแก้คือเพิ่ม worker/priority ให้ OTP, ใส่ cooldown + status ที่ชัดใน UI และลด tracking สำหรับ OTP
สถานการณ์ 2: ผู้ใช้บางคน “ส่งแล้วไม่ถึง” ซ้ำ ๆ คนเดิมตลอด
ตรวจพบว่า recipient เคย hard bounce มาก่อน ระบบจึงใส่ใน suppression list UI ยังขึ้นว่า Sent เพราะแค่ enqueue สำเร็จ แต่จริง ๆ worker ถูกบล็อกไม่ให้ส่งออก วิธีแก้คือปรับสถานะใน UI ให้สื่อว่า “ไม่สามารถส่งได้” พร้อมให้ผู้ใช้แก้ไขอีเมล หรือมี workflow clear suppression แบบปลอดภัย
สถานการณ์ 3: ทุกโดเมนเริ่มไม่รับหลังเปลี่ยน provider
ตรวจพบ SPF record ไม่ได้อัปเดตให้ provider ใหม่ และ DKIM ยังชี้ไปคีย์เก่า อีเมลจำนวนมากถูกปลายทาง reject หรือเข้าจังค์ทันที วิธีแก้คือแก้ DNS, รอ propagation, และทำ warm-up อย่างถูกวิธีสำหรับโดเมน/IP ใหม่
สรุป: ดีบักแบบทีมโปรดักต์ = ไล่จาก “เราส่งจริงไหม” ไปจนถึง “ผู้รับเห็นที่ไหน”
ปัญหา “Sent but Not Received” จะไม่จบด้วยการเดาว่า “ผู้ใช้ดูไม่เป็น” หรือ “อีเมลปลายทางมีปัญหา” วิธีที่จบจริงคือการสร้างหลักฐานตามเส้นทาง: event → queue → provider accept → delivered/bounce → inbox placement แล้วสรุป root cause พร้อมปรับทั้งระบบและ UX ให้ผู้ใช้ไม่ต้องกดซ้ำจนทำให้สถานการณ์แย่ลง
ถ้าคุณทำให้ทุกข้อความตามรอยได้ด้วย message_id และมีสถานะที่สื่อความจริง ทีมจะลดงานดับไฟ ซัพพอร์ตตอบได้เร็วขึ้น และที่สำคัญคือผู้ใช้จะรู้สึกว่าโปรดักต์ “เชื่อถือได้” แม้จะมีเหตุขัดข้องเป็นครั้งคราว