JSON vs Protobuf สำหรับ API มือถือ: ขนาด ความเข้ากันได้ การดีบัก
เปรียบเทียบ JSON กับ Protobuf สำหรับ API บนมือถือ — ข้อดีข้อเสียเรื่องขนาด payload ความเข้ากันได้ และการดีบัก พร้อมกฎปฏิบัติสำหรับการเลือกฟอร์แมตข้อความหรือไบนารี

ทำไมรูปแบบ API ถึงสำคัญสำหรับแอปมือถือ
แอปมือถืออาจรู้สึกช้าแม้ว่า backend จะตอบสนองเร็ว เหตุผลที่พบบ่อยไม่ใช่เวลาบนเซิร์ฟเวอร์ แต่มาจากสิ่งรอบข้าง: ความหน่วงของเครือข่ายเซลลูลาร์ สัญญาณอ่อน การลองใหม่ และเวลาที่โทรศัพท์ต้องตื่นเพื่อเชื่อมต่อวิทยุเครือข่าย หากหนึ่งหน้าจอเรียก API สามครั้ง คุณต้องจ่ายค่ารอบการเดินทางนั้นสามครั้ง
รูปแบบข้อมูลยังส่งผลต่อสิ่งที่เกิดขึ้นหลังจากไบต์มาถึง แอปยังต้องแปลงผลลัพธ์ ตรวจสอบความถูกต้อง และแมปเป็นโมเดล UI งานนี้ใช้ CPU ซึ่งมีผลต่อแบตเตอรี่ บนโทรศัพท์เก่าหรือเมื่อแอปรันในเบื้องหลัง ความไม่มีประสิทธิภาพเล็กน้อยจะสะสม
ขนาด payload คือจำนวนไบต์ที่ส่งทางสายสำหรับคำขอและการตอบกลับ รวมถึงชื่อฟิลด์และอักขระโครงสร้าง ขนาดเล็กกว่ามักหมายถึงดาวน์โหลดเร็วขึ้นบนเครือข่ายแย่และใช้ข้อมูลน้อยลงบนแผนที่จำกัด นอกจากนี้ยังช่วยลดการใช้แบตเพราะวิทยุจะเปิดทำงานน้อยลงและ CPU ต้องแปลงข้อมูลน้อยลง
การเลือกฟอร์แมตส่งผลต่อวิธีที่คุณสามารถพัฒนา API ได้อย่างปลอดภัย การเปิดตัวแอปมือถือช้ากว่าเว็บ: ผู้ใช้อัปเดตช้า บางคนไม่อัปเดตเลย และการตรวจสอบจากสโตร์อาจล่าช้า หากคุณปล่อยการเปลี่ยนแปลงที่ทำให้ไคลเอ็นต์เก่าพัง คุณอาจต้องรองรับหลายเวอร์ชันพร้อมกันภายใต้ความกดดัน
การดีบักก็สำคัญเหมือนกัน ด้วย JSON คุณมักอ่าน payload ในล็อกและเห็นปัญหาได้เร็ว ด้วยฟอร์แมตไบนารีอย่าง Protobuf คุณมักต้องมีสคีมาและเครื่องมือที่เหมาะสมเพื่อถอดรหัสสิ่งที่เกิดขึ้น
ในทางปฏิบัติการตัดสินใจนี้ส่งผลต่อเวลาการโหลดต่อหน้าจอบนเครือข่ายไม่ดี การใช้ข้อมูลและแบต วิธีการเพิ่มฟิลด์โดยไม่ทำให้แอปเก่าเสีย และความเร็วในการตรวจสอบข้อผิดพลาด
JSON และ Protobuf แบบเข้าใจง่าย
JSON และ Protobuf เป็นสองวิธีในการบรรจุข้อมูลแบบเดียวกันเพื่อให้แอปและเซิร์ฟเวอร์เข้าใจความหมายของข้อความ ลองคิดว่าเป็นการส่งโน้ตที่เขียนด้วยมือ (JSON) หรือบาร์โค้ดกะทัดรัด (Protobuf)
กับ JSON ข้อมูลถูกส่งเป็นข้อความโดยมีชื่อฟิลด์รวมอยู่ทุกครั้ง วัตถุผู้ใช้เรียบง่ายอาจเป็น {\"id\": 7, \"name\": \"Sam\"} มันอ่านได้เลย ทำให้ง่ายต่อการตรวจสอบในล็อก คัดลอกใส่บั๊กรีพอร์ต หรือทดสอบด้วยเครื่องมือพื้นฐาน
กับ Protobuf ข้อมูลถูกส่งเป็นไบต์ไบนารี แทนที่จะซ้ำชื่อฟิลด์อย่าง "id" และ "name" ทางฝั่งต่างๆตกลงกันล่วงหน้าว่าฟิลด์ 1 หมายถึง id และฟิลด์ 2 หมายถึง name ข้อความจึงเล็กลงเพราะเป็นค่าส่วนใหญ่บวกแท็กตัวเลขสั้นๆ
ข้อแตกต่างระหว่างข้อความกับไบนารี แบบไม่ต้องทฤษฎี
ข้อแลกเปลี่ยนเชิงปฏิบัติง่าย ๆ คือ:
- JSON อธิบายตัวเอง: ข้อความมีชื่อฟิลด์
- Protobuf ขับเคลื่อนด้วยสคีมา: ความหมายมาจากไฟล์นิยามที่ใช้ร่วมกัน
- JSON อ่านและแก้ไขด้วยมือได้ง่าย
- Protobuf กะทัดรัดและสม่ำเสมอ แต่ต้องใช้เครื่องมือเพื่ออ่าน
สคีมาคือสิ่งที่นิยามโครงสร้าง ด้วย Protobuf ทีมมักถือสคีมาเป็นสัญญาที่เวอร์ชันและเก็บให้สอดคล้องระหว่าง backend และไคลเอ็นต์มือถือ ในขณะที่ JSON สคีมาเป็นสิ่งเลือกได้ ทีมหลายทีมยังคงเขียนเอกสาร (เช่น OpenAPI) แต่ทางเทคนิค API สามารถส่งได้โดยไม่มีสคีมา
ในการทำงานประจำวันสิ่งนี้เปลี่ยนการทำงานร่วมกัน Protobuf ผลักดันให้คุณทำการเปลี่ยนแปลง API อย่างเป็นทางการ (เพิ่มฟิลด์ สำรองหมายเลขฟิลด์เก่า หลีกเลี่ยงการเปลี่ยนชื่อที่ทำให้พัง) JSON มักยอมให้เปลี่ยนแปลงแบบหลวม ๆ แต่ความยืดหยุ่นนี้อาจทำให้เกิดความประหลาดใจถ้าไคลเอ็นต์สมมติว่าฟิลด์จะมีเสมอหรือมีชนิดเดิมเสมอ
ในโลกจริง JSON พบมากใน REST API สาธารณะและการรวมแบบเร็ว Protobuf พบมากในบริการ gRPC การสื่อสารระหว่างเซอร์วิสภายใน และแอปมือถือที่เน้นประสิทธิภาพที่แบนด์วิดท์และความหน่วงมีความสำคัญ
ขนาด payload: อะไรเปลี่ยนจริงบนสาย
ขนาดดิบมีความสำคัญ แต่รายละเอียดสำคัญกว่า: ไบต์ใดซ้ำ ไบต์ใดบีบอัดได้ดี และส่งบ่อยแค่ไหน
ทำไม JSON มักใหญ่กว่า
JSON มีข้อความที่คนอ่านได้จำนวนมาก ต้นทุนที่ใหญ่ที่สุดมักเป็นคำรอบ ๆ ค่า:
- ชื่อฟิลด์ซ้ำในแต่ละวัตถุ ("firstName", "createdAt", "status")
- ตัวเลขถูกส่งเป็นข้อความ ทำให้ "123456" ใช้ไบต์มากกว่าจำนวนไบนารีที่กะทัดรัด
- การซ้อนลึกเพิ่มปีกกา คอมมา และเครื่องหมายคำพูด
- การพิมพ์สวย (pretty-print) เพิ่มช่องว่างที่ไม่ช่วยไคลเอ็นต์
ถ้า API ส่งรายการ 200 รายการและแต่ละรายการซ้ำชื่อฟิลด์ 10 ชื่อ ชื่อที่ซ้ำเหล่านั้นอาจครอง payload
ทำไม Protobuf มักเล็กกว่า
Protobuf แทนที่ชื่อฟิลด์ด้วยแท็กตัวเลขและใช้การเข้ารหัสไบนารีกะทัดรัด การเข้ารหัสแบบ packed เก็บตัวเลขที่ซ้ำกันได้อย่างมีประสิทธิภาพ (เช่น ID จำนวนมาก) และเพราะฟอร์แมตบนสายมีชนิด ตัวเลขและบูลีนมักเข้ารหัสด้วยไบต์น้อยกว่ารูปแบบข้อความใน JSON
โมเดลคิดง่าย ๆ: JSON จ่ายภาษีต่อฟิลด์ (ชื่อคีย์) มากกว่า Protobuf ที่จ่ายภาษีต่อฟิลด์น้อยกว่า (แท็ก)
การบีบอัดเปลี่ยนการเปรียบเทียบ
ด้วย gzip หรือ brotli JSON มักหดได้มากเพราะมีสตริงซ้ำ และชื่อฟิลด์ที่ซ้ำบีบอัดได้ดีมาก Protobuf ก็หดได้เช่นกัน แต่จะมีการซ้ำที่ชัดเจนน้อยกว่า ดังนั้นส่วนต่างอาจน้อยลง
ในทางปฏิบัติ Protobuf มักชนะเรื่องขนาด แต่ช่องว่างมักหดลงเมื่อเปิดการบีบอัด
เมื่อไหร่ที่ "เล็ก" สำคัญที่สุด
ขนาด payload สำคัญเมื่อคำขอเกิดบ่อยหรือเครือข่ายแย่ แอปที่เช็คอัปเดตทุก 10 วินาทีขณะโรมมิ่งอาจใช้ข้อมูลอย่างรวดเร็วแม้ว่าการตอบจะใหญ่เพียงเล็กน้อย ก็สำคัญสำหรับหน้าที่เรียกบ่อย (คำแนะนำการค้นหา dashboard สด) และผู้ใช้แบนด์วิดท์ต่ำ
ถ้าคุณเรียก endpoint แค่ไม่กี่ครั้งต่อเซสชัน ผลประหยัดมีจริงแต่ไม่มากนัก ถ้าคุณเรียกหลายร้อยครั้ง ความต่างเล็ก ๆ จะเห็นได้รวดเร็ว
ความเร็วและแบตเตอรี่: การแปลง การใช้ CPU และข้อจำกัดจริง
บนมือถือ เครือข่ายเป็นเพียงครึ่งเรื่อง การตอบต้องถูกถอดรหัส แปลงเป็นอ็อบเจ็กต์ และมักถูกเขียนลงฐานข้อมูลท้องถิ่น งานนี้ใช้ CPU และ CPU ใช้แบตเตอรี่
JSON เป็นข้อความ การแปลงต้องสแกนสตริง จัดการช่องว่าง แปลงตัวเลข และจับคู่ชื่อฟิลด์ Protobuf เป็นไบนารี มันข้ามงานส่วนใหญ่เหล่านั้นและให้ค่าสถานะที่ใกล้เคียงกับค่าที่แอปต้องการ ในหลายแอปหมายถึง CPU น้อยลงต่อการตอบ โดยเฉพาะกับ payload ที่ซ้อนลึกหรือรายการที่มีชื่อฟิลด์ซ้ำเต็ม
"เร็วกว่า" หมายถึงอะไรบนโทรศัพท์
คุณจะรู้สึกถึงค่า parsing โดยเฉพาะช่วง cold start และบนอุปกรณ์ราคาถูก หากแอปเปิดและโหลด feed ขนาดใหญ่ การถอดรหัสช้ากว่าอาจแสดงเป็นหน้าจอว่างนานขึ้นหรือการตอบสนองแรกช้ากว่า
อย่าสมมติว่า Protobuf จะแก้ปัญหาประสิทธิภาพโดยอัตโนมัติ ถ้าการตอบมีขนาดเล็ก หรือคอขวดคือรูปภาพ การจับมือ TLS การเขียนฐานข้อมูล หรือการเรนเดอร์ UI การเลือกฟอร์แมตอาจไม่ช่วย
ความสามารถในการประมวลผลฝั่งเซิร์ฟเวอร์ก็สำคัญ
การเข้ารหัสและถอดรหัสเกิดขึ้นบนเซิร์ฟเวอร์ด้วย Protobuf อาจลด CPU ต่อคำขอและเพิ่ม throughput ซึ่งช่วยเมื่อไคลเอ็นต์จำนวนมากดึงข้อมูลบ่อย แต่ถ้าระยะเวลาบน backend ถูกครอบงำด้วยการคิวรีฐานข้อมูล แคช หรือโลจิกธุรกิจ ความต่างอาจเล็กน้อย
ในการวัดอย่างยุติธรรม ให้ควบคุมทดสอบ: ใช้โมเดลข้อมูลและจำนวนเรคคอร์ดเดียวกัน จับคู่การตั้งค่าการบีบอัด (หรือปิดการบีบอัดทั้งคู่) ทดสอบบนเครือข่ายมือถือสมจริง (ไม่ใช่แค่ Wi-Fi เร็วของออฟฟิศ) และวัดเวลาจากต้นทางถึงปลายทางรวมถึง CPU สำหรับการถอดรหัส (ไม่ใช่แค่ดาวน์โหลด) รวมอุปกรณ์ราคาถูกอย่างน้อยเครื่องหนึ่ง
กฎง่าย ๆ: ฟอร์แมตไบนารีคุ้มเมื่อคุณส่งข้อมูลเชิงโครงสร้างจำนวนมากบ่อยครั้ง และคุณสามารถแสดงให้เห็นว่าเวลา parsing เป็นส่วนสำคัญของความหน่วงหรือการใช้แบต
ความเข้ากันได้ย้อนหลัง: การพัฒนา API อย่างปลอดภัย
ความเข้ากันได้ย้อนหลังหมายถึงไคลเอ็นต์เวอร์ชันเก่ายังคงทำงานหลังจากเซิร์ฟเวอร์อัปเดต บนมือถือเรื่องนี้สำคัญกว่าบนเว็บเพราะผู้ใช้ไม่อัปเดตทันที คุณอาจมีแอปเวอร์ชัน 3–4 อยู่พร้อมกัน
กฎปฏิบัติคือทำให้การเปลี่ยนแปลงเป็นแบบเพิ่มได้ เซิร์ฟเวอร์ควรยอมรับคำขอเก่าและส่งการตอบที่ไคลเอ็นต์เก่าเข้าใจ
กับ JSON การเปลี่ยนแปลงแบบเพิ่มมักหมายถึงการเพิ่มฟิลด์ใหม่เป็น optional ไคลเอ็นต์เก่ามักละเลยฟิลด์ที่ไม่ใช้ได้ ดังนั้นจึงปลอดภัยบ่อยครั้ง กับ JSON กับดักทั่วไปไม่ใช่ตัว JSON เอง แต่เป็นสมมติฐานที่ทำให้พัง: เปลี่ยนชนิดของฟิลด์ (string เป็น number), เปลี่ยนชื่อ, เปลี่ยนความหมายโดยไม่เปลี่ยนชื่อ, หรือทำให้ค่าคงที่กลายเป็นค่าที่เปิดกว้าง
กับ Protobuf ความเข้ากันได้เข้มงวดกว่าและเชื่อถือได้มากขึ้นถ้าทำตามกฎ หมายเลขฟิลด์คือสัญญา ไม่ใช่ชื่อฟิลด์ หากลบฟิลด์อย่าใช้หมายเลขเดิมอีก ให้สำรองไว้ อย่าปรับชนิดฟิลด์หรือสลับระหว่าง repeated และ non-repeated เพราะไคลเอ็นต์เก่าอาจพัง
การเปลี่ยนแปลงที่ปลอดภัยในทั้งสองฟอร์แมตมักเป็นแบบนี้:
- เพิ่มฟิลด์ optional ใหม่พร้อมค่าเริ่มต้นที่สมเหตุสมผล
- เพิ่มค่า enum และทำให้ไคลเอ็นต์จัดการค่าที่ไม่รู้จักได้
- คงฟิลด์เดิมในชนิดและความหมาย
- ประกาศ deprecate ก่อน แล้วค่อยลบหลังจากไคลเอ็นต์เก่าไม่อยู่แล้ว
การเวอร์ชันมีสองสไตล์ที่พบบ่อย การพัฒนาแบบเพิ่มได้เก็บ endpoint เดียวและขยายสคีมาเมื่อเวลาผ่านไป ซึ่งมักเหมาะกับมือถือ เวอร์ชันed endpoints (v1, v2) ช่วยเมื่อคุณต้องการการเปลี่ยนแปลงที่ทำให้พังจริง ๆ แต่ก็เพิ่มงานทดสอบและซัพพอร์ตเป็นสองเท่า
ตัวอย่าง: แอปของคุณแสดงรายการคำสั่ง หากต้องการเพิ่ม ETA ของการจัดส่ง ให้เพิ่ม delivery_eta เป็น optional อย่าใช้ status เพื่อรวม timestamp หากต้องการโมเดลใหม่ ให้พิจารณา endpoint v2 ขณะยังคงให้บริการ v1 จนกว่าจะมีผู้ใช้เก่าลดลง
การดีบักและการสังเกตการณ์: เห็นว่าผิดพลาดตรงไหน
เมื่อบางอย่างพังบนการเชื่อมต่อมือถือ คุณมักมีเบาะแสสามอย่าง: ข้อผิดพลาดฝั่งไคลเอ็นต์ บรรทัดล็อกจากเซิร์ฟเวอร์ และ trace ของคำขอ รูปแบบส่งผลต่อความเร็วที่เบาะแสเหล่านั้นแปรเป็นคำตอบ
JSON ตรวจสอบได้ง่ายเพราะอ่านได้ด้วยคน คุณสามารถคัดลอก body JSON จากล็อก การจับแพ็กเกจ หรือรายงานซัพพอร์ตแล้วเข้าใจทันที นั่นสำคัญเมื่อดีบักขณะปล่อย หรือเมื่อสมาชิกทีมที่ไม่ใช่แบ็กเอนด์ต้องยืนยันว่าแอปส่งอะไรจริง
Protobuf สามารถดีบักได้เช่นกัน แต่ต้องวางแผน payload เป็นไบนารีจึงต้องมีสคีมาและขั้นตอนถอดรหัส ทีมหลายทีมจัดการโดยการล็อกสรุปที่ถอดรหัสแล้วของฟิลด์สำคัญอย่างปลอดภัย (ไม่ใช่ไบต์ดิบ) พร้อมเมตาดาทาของคำขอ
ทำให้ Protobuf ดีบักได้จริง
นิสัยที่ช่วยได้มาก:
- บันทึกสรุปที่ถอดรหัสแล้ว (เช่น: user_id, request_type, item_count) ไม่ใช่ข้อความทั้งหมด
- เก็บไฟล์ .proto เวอร์ชันไว้และให้คนที่รับผิดชอบเหตุการณ์เข้าถึงได้
- ใส่ request ID และ trace ID ในทุกการตอบและบรรทัดล็อก
- ใช้ชื่อ enum ชัดเจนและหลีกเลี่ยงการใช้ฟิลด์ซ้ำ ๆ เพื่อความหมายหลายอย่าง
- ตรวจสอบกฎธุรกิจตั้งแต่ต้นและส่งรหัสข้อผิดพลาดที่อ่านได้
การสังเกตการณ์ยังเกี่ยวกับการติดตามโดยไม่รั่วไหลข้อมูลส่วนตัว ด้วยทั้งสองฟอร์แมต ให้ตัดสินใจแต่เนิ่น ๆ ว่าอะไรควรล็อก อะไรต้อง redacted และอะไรไม่ควรออกจากอุปกรณ์ ข้อมูลส่วนบุคคลทั่วไปเช่น อีเมล เบอร์โทร ตำแหน่งที่แน่นอน และข้อมูลการชำระเงินควรถูกกรองก่อนเก็บล็อก
สถานการณ์ง่าย ๆ: ฝ่ายซัพพอร์ตรายงานว่าผู้ใช้ส่งฟอร์มไม่ได้บนเครือข่ายที่ไม่เสถียร ด้วย JSON คุณอาจเห็นได้ทันทีว่าขาดฟิลด์ country ในคำขอที่จับได้ ด้วย Protobuf คุณจะถึงข้อสรุปเดียวกันหากล็อกบันทึกสแนปช็อตที่ถอดรหัส เช่น country: unset พร้อมเวอร์ชันสคีมา
วิธีเลือก: กระบวนการตัดสินใจทีละขั้น
การเลือกระหว่าง JSON กับ Protobuf ไม่ใช่การตัดสินใจครั้งเดียวสำหรับทั้งองค์กร ทีมส่วนใหญ่ทำได้ดีกว่าถ้าตัดสินใจแยกตามพื้นที่ฟีเจอร์โดยอิงข้อมูลการใช้งานจริง
กระบวนการ 5 ขั้นง่าย ๆ
เริ่มจากการจัดกลุ่ม endpoints ให้สามารถวัดได้ ระบุการเรียกที่เกิดบนทุกหน้าจอและที่หายากหรือรันเบื้องหลัง วัดสิ่งที่คุณส่งวันนี้ (ขนาดการตอบเฉลี่ยและ p95 และความถี่การเรียกต่อผู้ใช้แอคทีฟ) แล้วพิจารณาความเป็นจริงของไคลเอ็นต์: โทรศัพท์ระดับล่าง เครือข่ายไม่เสถียร พฤติกรรมออฟไลน์ และความเร็วในการอัปเดตของผู้ใช้
จากนั้นเลือกตามกลุ่ม: เก็บ JSON ไว้ในจุดที่ความสามารถในการอ่านและการดีบักสำคัญ ใช้ Protobuf ในจุดที่ขนาดและความเร็ว parsing เป็นปัญหาที่พิสูจน์ได้ สุดท้ายรันพยานทดลองเล็ก ๆ: เปลี่ยนพื้นที่ที่มีทราฟิกสูง ส่งให้กลุ่มผู้ใช้จำกัด และเปรียบเทียบผลก่อนตัดสินใจมาตรฐาน
หลังวัด รูปแบบมักชัด: จำนวน endpoint เล็ก ๆ ที่ก่อให้เกิดการใช้ข้อมูลและเวลารอส่วนใหญ่ เหล่านี้คือผู้สมัครที่ดีที่สุดสำหรับฟอร์แมตไบนารี
สิ่งที่ควรมองหาในการทดลอง
กำหนดความสำเร็จก่อนสร้าง เมตริกที่มีประโยชน์ประกอบด้วย เวลาคำขอกลางและ p95 ไบต์ที่ถ่ายโอนต่อเซสชัน เซสชันที่ไม่มีการตกเครื่อง (crash-free), และเวลา CPU ที่ใช้ในการถอดรหัส (โดยเฉพาะบนอุปกรณ์เก่า)
ถ้าคุณมี endpoint feed ที่ถูกเรียก 30 ครั้งต่อวันและส่งรายการขนาดใหญ่ที่มีฟิลด์ซ้ำมาก Protobuf อาจคุ้มค่า ถ้าปัญหาหลักคือ “เราไม่รู้ว่าเกิดอะไรขึ้น” ในการซัพพอร์ต การเก็บ JSON ในส่วนนั้นอาจประหยัดเวลาได้มากกว่าต้นทุน
ความผิดพลาดที่ทีมมักทำ
ทีมมักถกเถียงเรื่องฟอร์แมตก่อนมีตัวเลข นำไปสู่การเปลี่ยนที่เพิ่มงานแต่แทบไม่เปลี่ยนความหน่วง แบต หรือค่าใช้ข้อมูล
รูปแบบทั่วไปคือเปลี่ยน JSON เป็น Protobuf เพราะคิดว่า “ไบนารีเล็กกว่า” แล้วพบว่าปัญหาจริงคือรูปภาพใหญ่เกินไป endpoints เรียกบ่อย หรือแคชชิงแย่ วัดก่อนบนอุปกรณ์จริงและเครือข่ายจริง ไม่ใช่แค่ Wi-Fi เร็ว
ข้อผิดพลาดที่พบบ่อย: เปลี่ยนฟอร์แมตโดยไม่มี baseline, ทำให้ไคลเอ็นต์พังระหว่างแก้สคีมาเล็ก ๆ (เปลี่ยนชื่อ เปลี่ยนชนิด หรือใช้หมายเลขฟิลด์ Protobuf ซ้ำ), ใช้ไบนารีทุกที่แม้ไม่จำเป็น, และมองข้ามประสบการณ์นักพัฒนาเมื่อดีบัก production อีกปัญหาพบบ่อยคือการตั้งค่าการบีบอัดและแคชผิดพลาดแล้วโทษ serialization format
ตัวอย่างในทางปฏิบัติ: ทีมย้าย feed ไป Protobuf และฉลอง payload เล็กลง 30% ใน staging แต่ใน production แอปยังรู้สึกช้าเพราะ feed เรียกห้า request แยกกัน ไม่มีการแคช และเซิร์ฟเวอร์เพิ่มฟิลด์โดยไม่จำเป็น ฟอร์แมตไม่ใช่ปัญหาหลัก
ตัวอย่างสถานการณ์: แอปมือถือที่อัปเดตบ่อย
จินตนาการแอปมือถือที่มีฟีเจอร์เหมือนแชท: ผู้ใช้เห็นรายการบทสนทนา ตัวบ่งชี้การพิมพ์ การแจ้งการส่ง และการอัปเดตโปรไฟล์เป็นครั้งคราว ข้อความมาถึงเป็นอัปเดตเล็ก ๆ บ่อยครั้ง และผู้ใช้จำนวนมากอยู่บนเครือข่ายไม่เสถียรที่การเชื่อมต่อขาดบ่อย
การตอบ JSON ทั่วไปสำหรับ “get latest updates” เริ่มจากเล็ก แล้วขยายเมื่อเวลาผ่านไป ตอนแรกอาจส่งข้อความ ผู้ส่ง และ timestamp ต่อมาหลายรีลีสเพิ่ม reactions, read states ต่ออุปกรณ์, ธงการดูแล และวัตถุผู้ใช้ที่ละเอียดขึ้น JSON ทำให้ง่ายส่ง แต่ payload อาจพองเพราะชื่อฟิลด์ซ้ำในแต่ละรายการและทีมมักเพิ่มบล็อก optional “เผื่อไว้”
{
"messages": [
{
"id": "m_1842",
"text": "On my way",
"sentAt": "2026-01-29T10:12:03Z",
"sender": {"id": "u_7", "name": "Maya"},
"reactions": [{"emoji": "👍", "count": 3}],
"readBy": ["u_2", "u_5"]
}
],
"typing": ["u_7"]
}
กับ Protobuf ข้อมูลเดียวกันมักเล็กกว่าเพราะฟิลด์ถูกเข้ารหัสเป็นแท็กตัวเลขและชนิดกะทัดรัด ซึ่งช่วยเมื่ออัปเดตบ่อยและผู้ใช้มีแผนข้อมูลจำกัด ข้อแลกเปลี่ยนคือการประสานงาน: คุณต้องมีสคีมา การสร้างโค้ด และกฎที่เข้มงวดขึ้นสำหรับการเปลี่ยนแปลง การดีบักจะเปลี่ยนจาก “อ่านในล็อกได้เลย” เป็น “ถอดรหัสด้วยสคีมาที่ถูกต้อง”
ผลลัพธ์ทั่วไปคือแนวทางผสม ทีมมักเก็บ endpoints เหล่านี้เป็น JSON เพราะคนมักตรวจสอบบ่อยและ payload ค่อนข้างเล็ก: การล็อกอิน การตั้งค่า feature flags และหลายหน้าสไตล์แอดมิน Protobuf มักเด่นเรื่องทราฟิกหนาแน่นเช่นการซิงค์ข้อความ อัปเดตแบบเพิ่มทีละน้อย presence และ typing events รายการบทสนทนาใหญ่ที่มีวัตถุซ้ำ และชุดข้อมูลวิเคราะห์
เพื่อให้การเปิดตัวปลอดภัย อย่าเปลี่ยนทุกอย่างพร้อมกัน รันทั้งสองฟอร์แมตคู่ขนาน (เช่น ผ่าน header ที่ขอ Protobuf) ตั้งค่าเริ่มต้นอย่างสมเหตุสมผล และรักษากฎความเข้ากันได้ให้เข้มงวด ใน Protobuf อย่าใช้หมายเลขฟิลด์ซ้ำ ใน JSON ให้เพิ่มฟิลด์เป็น optional และหลีกเลี่ยงการเปลี่ยนชนิดโดยเงียบๆ
เช็คลิสต์ด่วนและขั้นตอนถัดไป
ตัดสินใจนี้โดยอิงทราฟิกและความจริงของการปล่อย ไม่ใช่รสนิยม การเลือกฟอร์แมตมีค่าเมื่อมันลดความเจ็บปวดของผู้ใช้ (หน้าช้า เวลา timeout แบตหมด) หรือความเจ็บปวดของทีม (การเปลี่ยนที่ทำให้พัง ปัญหาดีบักยาก)
เช็คลิสต์คร่าว ๆ:
- การตอบใดมีขนาดเกินไม่กี่ร้อย KB เป็นประจำ หรือถูกเรียกหลายสิบครั้งต่อเซสชัน (feeds, chat, tracking, sync)?
- เวอร์ชันแอปเก่ายังใช้งานอยู่นานหลายเดือนหรือไม่?
- คุณสามารถบังคับวินัยสคีมาได้ทุกครั้งที่ API เปลี่ยนหรือไม่?
- ฝ่ายซัพพอร์ตและ QA ต้องคัดลอก วาง และตรวจสอบ payload เพื่อทำซ้ำปัญหาหรือไม่?
กฎการใช้งาน: ถ้า payload เล็กและคนต้องอ่านบ่อย JSON มักดีกว่าในช่วงแรก ถ้าคุณมี payload หนาแน่นและเรียกบ่อย (หรือเครือข่ายไม่น่าเชื่อถือ) และคุณรักษาสคีมาเข้มงวด Protobuf อาจคุ้มค่า
แผนขั้นตอนถัดไปที่ตรงไปตรงมา:
- เลือก endpoint หนึ่งที่มีงานมาก (home feed หรือ sync)
- ทำให้มันรองรับทั้ง JSON และ Protobuf โดยมีฟิลด์และพฤติกรรมเหมือนกัน
- วัดขนาดบนสาย เวลา parsing บนโทรศัพท์ระดับกลาง อัตราความผิดพลาด และเวลาที่ต้องใช้ในการดีบัก
- เขียนนโยบายความเข้ากันได้สำหรับวิธีเพิ่มและ deprecate ฟิลด์ และวิธีที่ไคลเอ็นต์จัดการฟิลด์ที่ไม่รู้จัก
ถ้าต้องการทดลองเร็ว AppMaster (appmaster.io) สามารถสร้าง backend API และแอปจากโมเดลข้อมูลที่กำหนด ช่วยให้รันพิลอตแบบข้างเคียงและทำซ้ำการเปลี่ยนแปลงสคีมาได้โดยไม่ต้องเขียนโค้ดจำนวนมากด้วยมือ
คำถามที่พบบ่อย
Default to JSON if you’re optimizing for speed of development and easy debugging. Switch to Protobuf when you have high-frequency endpoints or large structured responses where bytes and parsing time clearly affect screen load time, data usage, or battery.
Round trips are often the real cost on mobile. If one screen triggers multiple calls, cellular latency and retries can dominate, even if the server is fast. Reducing the number of requests and the bytes per request usually matters more than shaving a few milliseconds off backend execution.
Payload size is the total bytes sent for a request and response, including field names and structural characters. Smaller payloads usually download faster on weak networks, use less data, and can reduce battery drain because the radio stays active for less time and the phone does less work parsing.
JSON repeats field names and encodes numbers as text, so it usually sends more bytes. Protobuf uses numeric tags and binary types, so it tends to be smaller, especially for lists with many repeated fields. With compression enabled, the size gap often shrinks but Protobuf commonly still wins.
Not always. If responses are small or the bottleneck is images, TLS handshakes, database writes, or UI rendering, the format change may have little impact. Protobuf tends to help when you send lots of structured data frequently and decoding time is a noticeable part of end-to-end latency.
JSON parsing costs CPU because the phone has to scan text, match field names, and convert values like numbers and dates. Protobuf decoding is typically more direct and consistent, which can reduce CPU work. The benefit shows up most on low-end devices, cold starts, and large nested payloads.
Additive changes are the safest for both formats: add new optional fields with sensible defaults and keep existing fields stable. With JSON, breaking changes often come from renames or type changes. With Protobuf, don’t reuse removed field numbers and avoid type changes to keep older clients working.
JSON is easy to inspect directly in logs and captures, which speeds up troubleshooting. Protobuf can be debuggable too, but you need the schema and decoding tooling, and it helps to log a safe decoded summary of key fields rather than raw bytes. Plan this before incidents happen.
Pick one high-traffic endpoint and implement it in both formats with the same data and compression settings. Measure p50/p95 latency, bytes transferred per session, decode CPU time on at least one low-end phone, and error rates on real cellular networks. Decide based on those numbers, not assumptions.
Keep JSON for endpoints where humans frequently inspect payloads or traffic is low, like auth flows, settings, and feature flags. Use Protobuf where traffic is heavy and repetitive, like feeds, chat sync, presence updates, or analytics batches. Many teams succeed with a split approach instead of a full switch.


