Quota คือ cost lever ไม่ใช่แค่ feature gate: ออกแบบ metered pricing ที่ยั่งยืน
ถ้า SaaS ของคุณเสียเงินจริงทุกครั้งที่ผู้ใช้กด action หนึ่งครั้ง quota ไม่ใช่ปุ่มจำกัดความสนุก แต่คือกลไกกัน margin ติดลบ มาดูวิธีผูก pricing เข้ากับ cost จริง แยกโหมด "เราจ่าย" ออกจาก "ลูกค้าจ่าย" และวัด headroom ก่อน scale
อ่าน ~8 นาที
ปัญหา: quota ที่ตั้งจากความรู้สึก ไม่ใช่จาก cost จริง
ตอนผมออกแบบราคาให้ระบบ SaaS ไทยตัวหนึ่ง ผมเกือบทำพลาดแบบที่หลายคนทำ คือมองว่า quota เป็นแค่ feature gate — ปุ่มที่เอาไว้บีบให้คนอัปเกรด แพ็กถูกให้น้อย แพ็กแพงให้เยอะ ตั้งตัวเลขจากความรู้สึกว่า "น่าจะพอดี" แล้วก็จบ
แต่ระบบนี้มีจุดต่างสำคัญ คือทุกครั้งที่ระบบส่งแจ้งเตือนออกไปหาผู้ใช้ปลายทาง มันยิงผ่าน third-party รายหนึ่งที่คิดเงินเราเป็นรายข้อความเมื่อเกินโควตาฟรี แปลว่าทุก action ที่ดูฟรีในสายตาผู้ใช้ มันมี variable cost ติดตัวจริงที่เราต้องจ่าย นี่คือแก่นของบทความ: เมื่อ SaaS ของคุณมี cost ต่อ action ที่ผันไปตามการใช้งาน quota เลิกเป็นเรื่อง UX ทันที มันกลายเป็นเส้นกั้น margin ไม่ให้ติดลบ และถ้าตั้งผิด คุณจะยิ่งขายยิ่งขาดทุน
Variable cost SaaS: ทำไม unit economics ต่างจาก SaaS ปกติ
SaaS แบบคลาสสิก (เช่นเก็บโน้ต จัดตาราง) มี marginal cost ต่อ action ใกล้ศูนย์ ผู้ใช้กด 10 ครั้งหรือ 10,000 ครั้งต่อเดือน server cost แทบไม่ขยับ ราคาจึงตั้งจาก "value ที่ลูกค้ารับรู้" ได้เต็มที่ ไม่ต้องกลัว cost ไล่ตาม
แต่พอมี action ใดที่ผูกกับ vendor ภายนอกที่คิดเงินเป็นราย unit — ส่งข้อความ, ส่ง SMS/อีเมล, เรียก LLM API, ประมวลผลรูป, ยิง webhook ที่มีค่าธรรมเนียม — โครง unit economics เปลี่ยนทันที เพราะตอนนี้กำไรต่อลูกค้าคือ:
margin = ราคาที่เก็บ − (fixed cost เฉลี่ย + variable cost × จำนวน action)
// ตราบใดที่ variable cost × action < ราคา → กำไร
// เกินเมื่อไหร่ → ลูกค้าคนนั้นกลายเป็นภาระทันที
สิ่งที่อันตรายคือ ลูกค้าที่ "ใช้เยอะ" มักเป็นลูกค้าที่คุณภูมิใจที่สุด แต่ในโมเดลแบบนี้ เขาคือคนที่กัดกำไรคุณหนักที่สุดถ้าราคาเป็น flat unlimited หลักคิดที่ผมยึด: ถ้ามี vendor คิดเงินเราต่อ action คุณต้องรู้ตัวเลข cost ต่อ action ให้แม่นก่อนตั้งราคาแม้แต่บาทเดียว
Quota เป็น margin protection ไม่ใช่แค่ UX gate
เมื่อ mindset เปลี่ยนจาก "quota = บีบให้อัปเกรด" เป็น "quota = เพดานที่ทำให้ลูกค้าคนหนึ่งไม่มีทางทำให้เราขาดทุน" การตั้งตัวเลขก็เปลี่ยนวิธีคิด แทนที่จะถามว่า "ให้เท่าไหร่ถึงจะดูใจกว้าง" ผมถามว่า "ที่ quota เพดานนี้ cost สูงสุดของลูกค้าคนนี้คือเท่าไหร่ และมันกินกำไรกี่เปอร์เซ็นต์"
ตัวอย่างการคิดแบบ margin-first ต่อ tier หนึ่ง:
// สมมติ vendor คิด ~0.04 บาท/ข้อความ เมื่อเกินโควตาฟรี
const costPerAction = 0.04;
const quota = 300; // เพดานข้อความ/เดือน ของ tier นี้
const price = 299; // ราคาที่เก็บ/เดือน
const worstCaseCost = costPerAction * quota; // = 12 บาท ถ้าใช้เต็มเพดาน
const marginFloor = price - worstCaseCost; // = 287 บาท (กำไรขั้นต่ำที่การันตี)
const costRatio = worstCaseCost / price; // = 0.04 → cost กินแค่ 4% ของราคา
// quota ที่ดี = แม้ลูกค้าใช้จนเต็มเพดาน costRatio ก็ยังต่ำพอรับได้
จุดสำคัญคือ quota การันตี margin floor ให้เรา ไม่ว่าลูกค้าจะใช้หนักแค่ไหน cost ต่อหัวก็ไม่มีทางทะลุเพดานที่เราคำนวณไว้ นี่คือความต่างระหว่าง quota ที่ตั้งเพื่อ conversion กับ quota ที่ตั้งเพื่อความอยู่รอด
สองโหมด: platform-paid (มี quota) vs customer-paid (unlimited, cost เรา ฿0)
insight ที่พลิกเกมให้ผมคือ: variable cost ไม่ได้ตกที่เราเสมอไป มันขึ้นกับว่า บัญชี vendor เป็นของใคร จากตรงนี้ผมแยก product เป็นสองโหมดตาม "ใครเป็นคนจ่าย cost ต่อ action":
- Platform-paid — ลูกค้าทุกคนยิง action ผ่านบัญชี vendor กลางของเราตัวเดียว cost ทั้งหมดตกที่เรา โหมดนี้ต้องมี quota เพราะทุกข้อความคือเงินในกระเป๋าเราที่ไหลออก
- Customer-paid — ลูกค้าเอาบัญชี vendor ของตัวเองมาผูก แล้ว action ทั้งหมดยิงผ่านบัญชีเขา บิลไปหาเขาโดยตรง cost ต่อเรา = ฿0 โหมดนี้ไม่ต้องจำกัด ให้ unlimited ได้สบายเพราะมันไม่ใช่ต้นทุนเรา
สังเกตว่า quota ไม่ใช่กติกาที่ผูกกับ "แพ็กแพงหรือถูก" แต่ผูกกับ "โครงสร้าง cost ของโหมดนั้น" ตรงๆ หลักการ transferable: metering ควรสะท้อนว่าใครแบก cost ไม่ใช่สะท้อนว่าลูกค้าจ่ายเราเท่าไหร่ พอแยกแบบนี้ enforcement code ก็ชัดขึ้นมาก — เช็ค quota เฉพาะ path ที่ยิงผ่านบัญชีกลางเท่านั้น ส่วน customer-paid ข้ามการนับไปเลย
Metered tier + free headroom: ตั้งราคาให้สะท้อน cost และรู้ว่าเมื่อไหร่ต้องเริ่มเก็บ
ในโหมด platform-paid ผมตั้งแต่ละ tier ให้ quota ไล่ขึ้นตามราคา และเช็คทุกชั้นว่า worst-case costRatio ยังต่ำ (ในเคสผมอยู่ราว 4-5% ของราคาแม้ใช้เต็มเพดาน) แปลว่าต่อให้ทุกคนใช้เต็ม margin ก็ยังหนา
แต่มีอีกชั้นที่คนมักลืม คือ vendor ส่วนใหญ่มี โควตาฟรีรวมก่อนเริ่มคิดเงิน สมมติ vendor ให้เราฟรี 35,000 action/เดือนแบบ flat ถ้าลูกค้าแต่ละคนใช้เฉลี่ย ~300 แปลว่า ~116 ลูกค้าแรกแทบไม่มี variable cost เลย เพราะทุกคนยังอยู่ใน pool ฟรีก้อนเดียวกัน:
const vendorFreePool = 35000; // action ฟรี/เดือน (flat จาก vendor)
const avgPerCustomer = 300; // ใช้เฉลี่ย/ลูกค้า/เดือน
const freeHeadroom = Math.floor(vendorFreePool / avgPerCustomer);
// ≈ 116 → ก่อนถึงลูกค้าคนที่ 116 marginal cost ต่อ action ≈ 0
// นัยยะ: ช่วง volume ต่ำ ยัง "ฟรี" ต่อเราได้จริง
// อย่าเพิ่งลงทุนสร้างระบบ quota enforcement ที่ซับซ้อนตอนนี้ —
// แต่ต้อง "รู้เส้น" นี้ และเริ่ม enforce ก่อนแตะ 116
บทเรียนคือ อย่าสับสนระหว่าง "ตอนนี้ยังฟรีอยู่" กับ "โมเดลนี้ฟรีถาวร" free headroom เป็นของยืมชั่วคราวจาก vendor pool พอ volume โต มันหมดแน่นอน สิ่งที่ต้องทำตั้งแต่วันแรกคือ วัด headroom เป็นตัวเลข แล้วตั้ง trigger ว่าเมื่อ active customer แตะกี่เปอร์เซ็นต์ของเส้นนั้น ต้องเปิด enforcement เต็มรูปแบบ
Quota enforcement ต้อง atomic และ observable
ตอน implement การนับ quota มี pitfall คลาสสิกคือ race condition ถ้าอ่านค่า count มาก่อน แล้วค่อยเช็ค แล้วค่อย +1 แยกเป็นสามสเต็ป สอง request ที่เข้ามาพร้อมกันจะอ่านได้ค่าเดียวกัน ผ่านเช็คทั้งคู่ แล้วยิงทะลุเพดาน — ลูกค้าใช้เกิน quota โดยที่เราแบก cost ส่วนเกินนั้นฟรีๆ นี่ไม่ใช่ bug เล็ก มันคือรูรั่วของ margin protection ที่เราอุตส่าห์วางไว้
ทางแก้คือทำ check-and-increment ให้เป็น atomic operation เดียว ให้ database เป็นคนตัดสินใจ ไม่ใช่ application code:
-- นับและกันทะลุใน statement เดียว (atomic)
-- เพิ่ม count เฉพาะเมื่อยังไม่ถึงเพดาน แล้วคืนแถวที่อัปเดตจริง
UPDATE quota_usage
SET used = used + 1
WHERE tenant_id = $1
AND period = $2
AND used < quota_limit -- guard อยู่ในเงื่อนไข ไม่ใช่ในโค้ด
RETURNING used;
-- ถ้าไม่มีแถวคืนมา = เต็มเพดานแล้ว → หยุดยิง action
-- ไม่มีทางที่สอง request จะ +1 ทะลุพร้อมกัน เพราะ row lock ของ UPDATE
อีกครึ่งที่ขาดไม่ได้คือ observability quota ที่นับเงียบๆ ไม่มีใครเห็น = ระเบิดเวลา ผมต้องมองเห็นได้ตลอดว่าลูกค้าแต่ละรายใช้ไปกี่เปอร์เซ็นต์ของเพดาน และ platform โดยรวมกิน vendor pool ไปเท่าไหร่แล้ว เพื่อจับสัญญาณก่อนที่ cost จะทะลุ ไม่ใช่มารู้ตอนได้บิลปลายเดือน หลักการ: ตัวเลขที่กระทบเงินต้องนับแบบกันชนกันได้ และต้องมองเห็นแบบ real-time เสมอ
Competitive angle: โหมด own-resource = ไม่จำกัด คือจุดที่ชนะ
สิ่งที่ผมเจอตอนดูคู่แข่งคือ ส่วนใหญ่เลือกสถาปัตยกรรมแบบบัญชี vendor กลางตัวเดียวให้ทุกลูกค้า ซึ่งเรียบง่ายกว่าตอน onboard แต่มันบังคับให้เขาต้อง cap ทุก tier แม้แต่แพ็กแพงสุด เพราะทุกข้อความคือ cost ของเขา เขา "ไม่จำกัด" ไม่ได้แม้อยากทำ นี่คือข้อจำกัดเชิงโครงสร้าง ไม่ใช่ทางเลือกการตลาด
พอเรามีโหมด customer-paid ที่ลูกค้าเอา resource ตัวเองมาผูก เราพูดได้เต็มปากว่า "ไม่จำกัด" ในโหมดนั้น เพราะ cost มันเป็น ฿0 สำหรับเราจริงๆ กลายเป็นจุดขายที่คู่แข่งลอกไม่ได้ถ้าไม่รื้อสถาปัตยกรรมทั้งระบบ บทเรียนเชิงกลยุทธ์: เมื่อ pricing ผูกกับ cost architecture การเลือก architecture ที่ยืดหยุ่นกว่าตั้งแต่แรกจะกลายเป็นข้อได้เปรียบที่ป้องกันตัวเองได้ — คู่แข่งที่ผูกตัวเองไว้กับ single-account model จะตามไม่ทันในแกนนี้
สรุป
- รู้ cost ต่อ action ก่อนตั้งราคา — ถ้ามี vendor คิดเงินต่อ unit unit economics ต่างจาก SaaS ปกติสิ้นเชิง
- quota = margin protection ไม่ใช่แค่ปุ่มบีบอัปเกรด ตั้งเพดานให้ worst-case cost ยังกินกำไรแค่หลักเปอร์เซ็นต์
- metering สะท้อน "ใครแบก cost" — โหมด platform-paid ต้องมี quota, โหมด customer-paid (ลูกค้าใช้ resource ตัวเอง) ให้ unlimited ได้เพราะ cost เรา ฿0
- วัด free headroom เป็นตัวเลข — vendor free pool ทำให้ช่วง volume ต่ำยังฟรีได้จริง แต่มันหมดแน่นอน ตั้ง trigger ก่อนถึงเส้น
- enforcement ต้อง atomic — check-and-increment ใน statement เดียว กัน race ไม่ให้ยิงทะลุเพดานฟรีๆ และต้อง observable แบบ real-time
- architecture ที่ยืดหยุ่นเป็นข้อได้เปรียบเชิงแข่งขัน — โหมด own-resource ที่ให้ "ไม่จำกัด" ได้จริง คือจุดที่คู่แข่งแบบ single-account ลอกไม่ได้




