ทุกสิ่งที่ผมรู้เกี่ยวกับการออกแบบ API ที่ดี
(seangoedecke.com)- ในวิศวกรรมซอฟต์แวร์ API คือเครื่องมือหลัก และ API ที่ดีควรคุ้นเคยและเรียบง่ายจนแทบจะน่าเบื่อ
- เมื่อ API ถูกเปิดเผยออกไปแล้ว การเปลี่ยนแปลงจะทำได้ยาก จึงต้องยึดหลัก ไม่ทำลายสภาพแวดล้อมของผู้ใช้ (WE DO NOT BREAK USERSPACE)
- หากจำเป็นต้องเปลี่ยนจริง ๆ ก็ต้องมี การจัดการเวอร์ชัน (versioning) แต่สิ่งนี้เป็น สิ่งจำเป็นที่ไม่พึงประสงค์ เพราะเพิ่มความซับซ้อนและต้นทุนการดูแลรักษาอย่างมาก
- คุณภาพของ API ท้ายที่สุดแล้วขึ้นอยู่กับ คุณค่าของตัวผลิตภัณฑ์เอง และหากผลิตภัณฑ์ถูกออกแบบมาไม่ดี ก็ยากที่จะสร้าง API ที่ดีได้
- เพื่อความเสถียรและการขยายระบบ ควรคำนึงถึง การยืนยันตัวตนด้วย API key, idempotency, rate limit, การแบ่งหน้าแบบ cursor-based เป็นต้น
บทนำ: ความสำคัญและบริบทของการออกแบบ API
- หนึ่งในงานหลักของวิศวกรซอฟต์แวร์ยุคใหม่คือการ โต้ตอบกับ API
- ผู้เขียนเองก็มีประสบการณ์ในการออกแบบ/พัฒนา/ใช้งาน API ทั้งแบบสาธารณะและภายในองค์กร ในหลายรูปแบบ เช่น REST, GraphQL และเครื่องมือบรรทัดคำสั่ง
- คำแนะนำด้านการออกแบบ API ที่มีอยู่ในปัจจุบันมักหมกมุ่นกับแนวคิดที่ซับซ้อนเกินไป (เช่น นิยามของ REST, HATEOAS เป็นต้น)
- บทความนี้จึงสรุป หลักการออกแบบ API เชิงปฏิบัติ จากประสบการณ์จริง
สมดุลระหว่างความคุ้นเคยกับความยืดหยุ่น: เงื่อนไขแรกของ API ที่ดี
- API ที่ดีคือ API ที่ธรรมดาและน่าเบื่อ กล่าวคือ ควรมีวิธีใช้งานคล้ายกับ API อื่น ๆ ที่ผู้ใช้เคยพบมาก่อน
- ผู้ใช้มัก โฟกัสที่การบรรลุเป้าหมายของตนเองมากกว่าตัว API ดังนั้นจึงต้องออกแบบให้มีอุปสรรคในการเริ่มใช้งานต่ำ
- API ที่เปิดเผยออกไปแล้วนั้น เปลี่ยนแปลงได้ยากมาก จึงต้องระมัดระวังอย่างยิ่งตั้งแต่ขั้นตอนการออกแบบครั้งแรก
- นักพัฒนามักต้องการ API ที่กระชับที่สุดเท่าที่จะเป็นไปได้ แต่ก็ต้องคิดเผื่อความยืดหยุ่นระยะยาวอยู่เสมอ
- สุดท้ายแล้ว ประเด็นสำคัญคือ การหาสมดุลระหว่างความคุ้นเคยกับความยืดหยุ่นในระยะยาว
ห้ามทำลาย userspace เด็ดขาด (WE DO NOT BREAK USERSPACE)
- การ เพิ่มฟิลด์ เข้าไปในโครงสร้าง response เดิมนั้น โดยมากแล้วไม่ก่อปัญหา
- แต่ การลบฟิลด์ หรือเปลี่ยนชนิดข้อมูลและโครงสร้าง จะทำให้โค้ดของผู้ใช้ทุกคนพังทันที
- ผู้ดูแล API มี ความรับผิดชอบ ที่จะไม่ทำให้ซอฟต์แวร์ของผู้ใช้เดิมเสียหายโดยเจตนา
- แม้แต่การสะกดผิดของ HTTP header อย่าง
refererก็ยังไม่ถูกแก้ เพราะมี วัฒนธรรมในการรักษา userspace
เปลี่ยน API โดยไม่ทำให้พัง: กลยุทธ์การจัดการเวอร์ชัน
- ควรยอมรับ การเปลี่ยนแปลงแบบทำลายความเข้ากันได้ กับ API เฉพาะเมื่อจำเป็นจริง ๆ และในกรณีนั้น การจัดการเวอร์ชัน คือคำตอบ
- ควร เปิดให้เวอร์ชันเก่าและใหม่ทำงานพร้อมกัน เพื่อให้ผู้ใช้ค่อย ๆ ย้ายผ่านได้
- ตัวระบุเวอร์ชันอาจใส่ใน URL (
/v1/) หรือ header ก็ได้ ทำให้ผู้ใช้เลือกย้ายเมื่อพร้อมตามจังหวะของตัวเอง - การจัดการเวอร์ชันมีข้อเสียคือ ต้นทุนการดูแลรักษาที่มหาศาล (endpoint เพิ่มขึ้น, การทดสอบ, การซัพพอร์ต) และ ความสับสนของผู้ใช้
- ต่อให้มีชั้นแปลภายในแบบ Stripe ก็ยังหลีกเลี่ยงความซับซ้อนพื้นฐานนี้ไม่ได้
- การนำ API versioning มาใช้ควรเป็นทางเลือกสุดท้าย
ปัจจัยความสำเร็จของ API ขึ้นอยู่กับคุณค่าของผลิตภัณฑ์ล้วน ๆ
- API โดยเนื้อแท้แล้วเป็นเพียง อินเทอร์เฟซของผลิตภัณฑ์ธุรกิจจริง เท่านั้น
- API อย่าง OpenAI หรือ Twilio ก็สุดท้ายแล้ว สิ่งที่ผู้ใช้ต้องการคือ ความสามารถที่ API มอบให้โดยตรง
- หากเป็น ผลิตภัณฑ์ที่มีคุณค่า ผู้ใช้ก็จะยอมใช้ แม้ API จะใช้งานไม่สะดวกนักก็ตาม
- คุณภาพของ API เป็นคุณสมบัติแบบ “ส่วนต่าง” กล่าวคือ จะกลายเป็นปัจจัยในการเลือกก็ต่อเมื่อความสามารถหลักของคู่แข่งใกล้เคียงกัน
- ในทางกลับกัน หากผลิตภัณฑ์ ไม่มี API เลย ก็เป็นอุปสรรคใหญ่สำหรับผู้ใช้สายเทคนิค
ถ้าออกแบบผลิตภัณฑ์ไม่ดี API ก็ไม่มีทางดีได้
- ต่อให้มี API ที่ยอดเยี่ยมในเชิงเทคนิค แต่ถ้า ผลิตภัณฑ์ไม่มีศักยภาพทางตลาด ก็แทบไม่มีความหมาย
- ที่สำคัญกว่านั้นคือ หาก โครงสร้าง resource พื้นฐานไม่มีตรรกะหรือไร้ประสิทธิภาพ ปัญหานั้นก็จะสะท้อนออกมาที่ API ด้วย
- ตัวอย่างเช่น ระบบที่เก็บคอมเมนต์เป็น linked list จะทำให้แม้แต่การออกแบบแบบ RESTful ก็ออกมาอย่างเป็นธรรมชาติได้ยาก
- ปัญหาทางเทคนิคที่อาจซ่อนไว้ได้ใน UI จะถูกเปิดเผยออกมาทั้งหมดใน API และบังคับให้ผู้ใช้ต้องเข้าใจระบบมากเกินความจำเป็น
การยืนยันตัวตน (Authenticaton) และความหลากหลายของผู้ใช้
- ต้องรองรับ การยืนยันตัวตนด้วย API key ที่มีอายุใช้งานยาว
- แม้จะรองรับวิธีที่ปลอดภัยสูงกว่าอย่าง OAuth เพิ่มเติมได้ แต่ API key มี อุปสรรคในการเริ่มใช้งานต่ำกว่ามาก
- ผู้ใช้ API ไม่ได้มีแค่วิศวกร แต่ยังรวมถึงผู้ที่ไม่ใช่นักพัฒนา (ฝ่ายขาย, ผู้วางแผน, นักเรียน, นักพัฒนางานอดิเรก ฯลฯ) อีกมาก
- การบังคับใช้วิธีการยืนยันตัวตนที่ยากหรือซับซ้อน (เช่น OAuth) เป็น กำแพงสำหรับผู้ใช้ที่ไม่เชี่ยวชาญ
Idempotency และการจัดการ retry
- คำขอที่ก่อให้เกิดการกระทำ (เช่น การชำระเงิน, การเปลี่ยนสถานะ ฯลฯ) ต้องให้ความสำคัญกับความปลอดภัยในการ retry เมื่อเกิดความล้มเหลว
- Idempotency คือการ รับประกันว่าแม้ส่งคำขอเดิมหลายครั้ง ผลลัพธ์จะถูกประมวลผลเพียงครั้งเดียว
- วิธีมาตรฐานคือส่ง “idempotency key” ผ่านพารามิเตอร์หรือ header เพื่อป้องกันการประมวลผลซ้ำ
- การเก็บ idempotency key ใช้เพียง ที่เก็บ key/value แบบง่าย ๆ อย่าง Redis ก็เพียงพอ และในกรณีส่วนใหญ่จะตั้งหมดอายุเป็นระยะ ๆ ก็ได้
- โดยทั่วไปไม่จำเป็นสำหรับคำขออ่าน/ลบ (แนวทางแบบ REST)
ความปลอดภัยของ API และการจำกัดอัตรา (Rate limiting)
- คำขอ API ผ่านโค้ดสามารถเกิดขึ้นได้เร็วกว่าการกระทำของผู้ใช้มาก
- API ที่ปล่อยออกไปอย่างไม่ทันคิดเพียงรายการเดียว อาจถูกนำไปใช้ในทางที่ไม่ได้ตั้งใจ (เช่น ระบบแชตขนาดใหญ่)
- การจำกัดอัตรา (ratelimit) เป็นสิ่งจำเป็น และควรเข้มงวดมากขึ้นกับงานที่มีต้นทุนสูง
- ควรพิจารณาทางเลือกอย่างการปิดการใช้งาน API ชั่วคราวสำหรับลูกค้าบางรายโดยเฉพาะ (killswitch)
- ควรแจ้งข้อมูลการจำกัดอัตราผ่าน response header (
X-Limit-Remaining,Retry-Afterเป็นต้น)
กลยุทธ์การแบ่งหน้า (Pagination)
- หากต้องคืนข้อมูลจากชุดข้อมูลขนาดใหญ่ (เช่น ticket หลายล้านรายการ) อย่างมีประสิทธิภาพ การแบ่งหน้าเป็นสิ่งจำเป็น
- การแบ่งหน้าแบบ offset-based นั้นง่าย แต่เมื่อข้อมูลมีจำนวนมากจะยิ่งช้าลงเรื่อย ๆ
- การแบ่งหน้าแบบ cursor-based มีประสิทธิภาพแม้กับชุดข้อมูลขนาดใหญ่มาก โดยไม่ทำให้ประสิทธิภาพ query แย่ลง
- แม้ cursor-based จะทั้งพัฒนาและใช้งานยากกว่าเล็กน้อย แต่ในระยะยาวมีแนวโน้มสูงว่าจะกลายเป็นการเปลี่ยนแปลงที่จำเป็น
- การใส่ฟิลด์อย่าง
next_pageไว้ใน response เพื่อบอก cursor สำหรับคำขอถัดไปอย่างชัดเจนถือเป็นแนวทางที่เหมาะสม
มุมมองต่อฟิลด์แบบเลือกได้และ GraphQL
- ฟิลด์ที่มีต้นทุนสูงหรือช้า ควรถูกตัดออกจาก response เริ่มต้น และค่อยเพิ่มเข้ามาแบบเลือกได้เมื่อจำเป็น
- สามารถรองรับการใส่ข้อมูลที่เกี่ยวข้องผ่านพารามิเตอร์อย่าง
includes - GraphQL มีข้อดีด้านความยืดหยุ่นของโครงสร้างข้อมูล แต่ก็มีปัญหาเรื่อง การเข้าถึงที่ยากขึ้นสำหรับผู้ที่ไม่ใช่นักพัฒนา, ความซับซ้อนของ caching/edge case, และความยากของการพัฒนาฝั่ง backend
- จากประสบการณ์จริง การใช้ GraphQL ควร จำกัดเฉพาะกรณีที่จำเป็นจริง ๆ
ลักษณะเฉพาะของ API ภายในองค์กร
- API ภายในองค์กรมีเงื่อนไขหลายอย่างที่ต่างจาก API ภายนอก (แบบเปิดสู่สาธารณะ)
- ผู้ใช้ส่วนใหญ่เป็น วิศวกรซอฟต์แวร์มืออาชีพ จึงสามารถใช้การยืนยันตัวตนที่ซับซ้อนกว่า หรือยอมรับการเปลี่ยนแปลงแบบทำลายความเข้ากันได้มากกว่าได้
- ถึงอย่างนั้น หลักการออกแบบเพื่อ idempotency, การป้องกันอุบัติเหตุ, และการลดภาระการปฏิบัติการ ก็ยังใช้ได้อยู่
สรุป
- API เปลี่ยนยาก แต่ควรใช้งานง่าย
- การไม่ทำลาย userspace คือหน้าที่สำคัญที่สุดของผู้ดูแล API
- การจัดการเวอร์ชันของ API มีต้นทุนสูง จึงควรใช้เป็นทางเลือกสุดท้ายเท่านั้น
- ท้ายที่สุดแล้ว คุณภาพของ API ถูกกำหนดโดยคุณค่าที่แท้จริงของผลิตภัณฑ์
- หากผลิตภัณฑ์ถูกออกแบบผิดพลาด การพยายามชดเชยในระดับ API ก็มีขีดจำกัดมาก
- การ รองรับวิธียืนยันตัวตนที่เรียบง่าย, การมี idempotency สำหรับคำขอประเภท action ที่จำเป็น, และมาตรการด้านเสถียรภาพอย่าง rate limit/pagination ล้วนสำคัญ
- API ภายในมีแนวทางต่างกันตามวัตถุประสงค์และกลุ่มผู้ใช้ แต่ก็ยังต้องออกแบบอย่างรอบคอบเช่นเดิม
- REST, JSON หรือสเปกอย่าง OpenAPI ไม่ใช่ประเด็นแก่นแท้ สิ่งที่สำคัญกว่าคือการทำเอกสารให้ชัดเจน
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
คำแนะนำที่ว่า "อย่าทำให้ userspace พังเด็ดขาด" นั้นโด่งดังมาก แต่คอมเมนต์นี้ก็ชี้ให้เห็นด้านตรงข้ามได้ดีเช่นกัน นั่นคือ "kernel API สามารถพังได้โดยไม่ต้องแจ้งล่วงหน้า" ประเด็นสำคัญไม่ใช่ "อย่าทำให้ทุก API พังแบบส่งเดช" แต่คือความสมดุลที่ละเอียดอ่อนว่า "ให้ห้ามพังเฉพาะส่วนที่ประกาศว่าจะคงเสถียรภาพไว้เท่านั้น"
ต่อให้ Linux kernel จะไม่ทำให้ userspace พัง แต่ GNU libc กลับทำลายความเข้ากันได้ของ userspace ค่อนข้างบ่อย ผลคือ user space บน Linux ก็ยังพังได้บ่อยอยู่ดีไม่ว่านักพัฒนา kernel จะพยายามแค่ไหน โปรแกรมและไลบรารีที่ build ด้วย libc เวอร์ชันใหม่ก็มักรันบน libc รุ่นเก่าไม่ได้อย่างถูกต้อง สุดท้ายจึงกลายเป็นสภาพที่ต้องอัปเกรดทุกองค์ประกอบพร้อมกันทั้งหมด แบบน่าประหลาดใจนิด ๆ คือ Windows แก้ปัญหานี้ด้วยแนวทาง redistributable ไปตั้งแต่หลายสิบปีก่อนแล้ว
เป็นที่รู้กันดีว่า Linux ไม่มี public driver API ที่เสถียร และได้ยินมาว่านี่เองเป็นแรงจูงใจให้ Google พัฒนา Fuschia OS เรียกได้ว่า Linux มีทิศทางคนละแบบต่อ user space กับฮาร์ดแวร์
ดูเหมือนผู้เขียนจะไม่ค่อยชอบ API แบบอิงเวอร์ชันนัก แต่ฉันกลับแนะนำเสมอว่าควรใส่การจัดการเวอร์ชันตั้งแต่ตอนเริ่มสร้างแอปเลย เพราะเราไม่อาจคาดเดาอนาคตได้ สักวันหนึ่งก็เลี่ยงไม่ได้ที่จะมีการเปลี่ยนแปลงแบบ breaking change เกิดขึ้นกับคุณจากปัจจัยภายนอก
ที่จริงฉันคิดว่าผู้เขียนก็แนะนำเรื่อง versioning เหมือนกัน เพราะในเนื้อหามีประโยคว่า "เวอร์ชันคือวิธีเปลี่ยน API อย่างมีความรับผิดชอบ" ดังนั้นก็เท่ากับสนับสนุนการจัดการเวอร์ชันนั่นเอง เพียงแต่บอกว่าการย้ายไปเวอร์ชันใหม่ควรเป็นทางเลือกสุดท้าย
ฉันเห็นด้วยกับความเห็นที่ว่าไม่จำเป็นต้องใส่ "v1" ลงไปใน endpoint จริง ๆ แล้วเมื่อ API เติบโตขึ้น สิ่งที่มักเกิดขึ้นคือเริ่มจากพยายามเพิ่มฟิลด์หรือออปชันให้ endpoint เดิมเพื่อรักษา backward compatibility เอาไว้ก่อน และถ้าจำเป็นต้องทำสิ่งที่ไม่เข้ากันโดยสิ้นเชิง ก็มักจะตั้งชื่อ endpoint ใหม่ไปเลยแล้วสร้าง endpoint ใหม่ทั้งชุด (ไม่ใช่ /v2) ถ้าต้องเปลี่ยนทั้ง API จริง ๆ ก็มักจะเลิกบริการเดิมแล้วเปิดบริการใหม่ที่ตั้งชื่อใหม่ตั้งแต่ต้น ตลอด 25 ปีที่ทำงานมา ฉันเคยเห็นบริการที่ใช้ "/v1" กับ "/v2" ควบคู่กันอยู่แค่ครั้งเดียว
ฉันไม่คิดว่าเจตนาของผู้เขียนคือห้ามใส่ /v1 ใน endpoint ตั้งแต่แรก ประเด็นคือควรพยายามอย่างที่สุดไม่ให้ต้องมีเวอร์ชันใหม่ (/v2) เพราะพอมี /v2 แล้ว ทุกครั้งที่แก้บั๊กก็ต้องแก้โค้ดทั้งสองฝั่ง และเงื่อนไขแตกแขนงจะเพิ่มขึ้นแบบทวีคูณจนโค้ดเบสกลายเป็นสปาเกตตี สุดท้ายการที่ต้องรองรับหลายเวอร์ชันก็สะท้อนว่าแบบ /v1 เดิมไม่ได้เผื่อความเข้ากันได้ในอนาคตไว้ดีพอ
ฉันคิดว่าค่อยเพิ่ม versioning ทีหลังก็ไม่มีปัญหา เช่น เริ่มจาก /api/posts ก่อน แล้วค่อยเพิ่มเวอร์ชันถัดไปเป็น /api/v2/posts ก็เพียงพอแล้ว
ฉันไม่เห็นด้วยกับวิธีฝังเวอร์ชันไว้ตั้งแต่แรก เพราะมันทำให้การใช้หลายเวอร์ชันพร้อมกันเกิดขึ้นบ่อยจริง ๆ ซึ่งฉันคิดว่าไม่ใช่สิ่งที่ดีกว่า
บทความนี้มีประโยชน์มาก ฉันขอเพิ่มคำแนะนำอีกข้อหนึ่ง คุณภาพของ API มักจะแปรผกผันกับความยากในการได้มาซึ่งเอกสาร API ถ้าต้องเซ็นสัญญาก่อนถึงจะได้เอกสาร ก็ถือได้เลยว่า API นั้นคุณภาพแย่มาก
ผู้เขียนบอกว่าแทนที่จะเก็บ idempotency key แยกไว้ในตาราง comment ให้เอาไปใส่ใน key/value store อย่าง Redis แต่ฉันสงสัยว่าวิธีนี้จะรับประกัน idempotency ได้แน่นอนจริงหรือไม่ในทุกกรณีที่ล้มเหลว สมมติว่าเซิร์ฟเวอร์ทำ conditional write อย่าง
SET key 1 NXแล้วพบว่ามี key อยู่แล้ว ก็ควรต้องข้ามการสร้างคอมเมนต์ไป แต่ในจังหวะนั้นคำขอก่อนหน้าอาจยังไม่ได้ถูกเขียนลง DB จริงก็ได้ การเก็บ idempotency key ควรถูก commit พร้อมงานจริงในระดับ transaction เดียวกัน และควร rollback ได้เมื่อจำเป็น ท้ายที่สุดแล้วแก่นของ idempotency key ควรเป็น "ID เฉพาะของงานหรือคำขอนี้" เช่น ควรเป็นตัวระบุระดับ resource ที่สอดคล้องกับแต่ละงานอย่าง “สร้างคอมเมนต์”, “อัปเดตคอมเมนต์” เป็นต้นข้อดีของ cursor-based pagination คือในมุมผู้ใช้ ต่อให้มีรายการใหม่เพิ่มเข้ามาระหว่างที่โหลดหน้าแล้วกดปุ่ม ‘ถัดไป’ ก็ไม่ต้องกลับไปเห็นรายการเดิมซ้ำ เพราะวิธี cursor จะจำ object ID สุดท้ายของหน้าก่อนหน้าไว้แล้วส่งรายการหลังจากนั้นมา จึงเหมาะอย่างยิ่งกับ infinite scroll ในทางกลับกัน วิธีนี้มีข้อเสียตรงที่ทำฟีเจอร์ “กระโดดไปหน้าที่ N” ได้ยาก
ทุกวันนี้พอพูดว่า "API" คนส่วนใหญ่มักนึกถึงการส่ง request ไปยังเว็บแอป ตั้งค่า parameter กับ header แล้วดึงข้อมูลกลับมา แต่เดิม API หมายถึง "Application Programming Interface" หรือก็คือ ‘อินเทอร์เฟซของโปรแกรมแอปพลิเคชัน’ คำนี้เริ่มถูกใช้ครั้งแรกในช่วงทศวรรษ 1940 และหลังจากนั้นจนถึงช่วงทศวรรษ 1990 ก็แทบไม่ได้ถูกใช้ในความหมายอื่นเลย ประวัติของ API ยาวนานเกิน 80 ปี และมีเอกสารเก่าแก่มากมาย ถ้าลองคิดดูว่าสมัยนั้นโปรแกรมเมอร์รับมือกับปัญหาอะไรและแก้กันอย่างไร ก็อาจมีส่วนที่ช่วยคุณได้ในปัจจุบันเช่นกัน
ฉันไม่เห็นด้วยกับความเห็นที่มองผู้ใช้ภายในว่าเป็นแค่ 'ผู้ใช้' ธรรมดา แม้คนเหล่านี้จะเป็นคนสายเทคนิคมากกว่าและมีโอกาสเป็นโปรแกรมเมอร์สูง แต่พวกเขาก็ยุ่งและจดจ่อกับโปรเจกต์ของตัวเอง จนมักไม่มีเวลาหรือพื้นที่พอจะรับมือกับการเปลี่ยนแปลงของ API ดังนั้นถ้าเป็นไปได้ ก็ควรทำการทดสอบแบบ "dogfooding" (ใช้งานจริงภายใน) ให้เพียงพอก่อนเปิดสู่ภายนอก พอเปิดให้ภายนอกใช้แล้ว ก็ต้องรักษาคำมั่นว่า ‘จะไม่ทำให้ userspace พัง’ ให้ได้
ถ้าเป็นผู้ใช้ภายใน ก็มักมีเครื่องมือ telemetry ที่ช่วยติดต่อโดยตรงและผลักดันการ migration ได้อยู่แล้ว ด้วยเหตุนี้การยกเลิก API version เก่าจึงทำได้ และทำให้การนำ versioning มาใช้อย่างมีกลยุทธ์มีความน่าสนใจมาก ฉันเคยมีส่วนร่วมกับการทำ API versioning จริง และเห็นผลชัดเจนว่ามันได้ผลเมื่อเทียบกับองค์กรที่โดยพื้นฐานไม่ใช้แนวทางนี้
ฉันคิดว่าแนวทาง versioning ช่วยแก้ปัญหานี้ได้ วิธีที่ดีที่สุดวิธีหนึ่งในการใส่ใจผู้ใช้ภายในคือร่วมกันทำงานกับสเปก และแชร์เวอร์ชันที่กำลังดำเนินการอยู่ของสเปกนั้นกับผู้มีส่วนได้ส่วนเสีย ต่อให้เป็นเอกสารที่อัปเดตต่อเนื่อง หากมีจุดอ้างอิงที่ชัดเจน ก็จะช่วยให้รับฟัง feedback ทั้งภายในและภายนอกได้ราบรื่น และตราบใดที่หลีกเลี่ยงความเสี่ยงเชิงนโยบายที่ไม่จำเป็น ก็ใช้งานได้มีประโยชน์มาก
แทนที่จะเก็บ idempotency key ไว้ใน redis ฉันคิดว่าถ้าทำได้ การเก็บ idempotency key ไว้ภายใน transaction เดียวกันกับที่บันทึกข้อมูลจริงจะมั่นใจกว่า
คำเตือนที่ว่า "อย่าทำให้ userspace พังเด็ดขาด" สำคัญมากจริง ๆ ช่วงหลังมานี้รู้สึกเสียดายกับ Spotify, Reddit, Twitter และรายอื่น ๆ ที่เมินหลักการนี้
อ้างอิงเพิ่มเติม ลิงก์ https://jcs.org/2023/07/12/api มีการรวบรวมคำแนะนำเกี่ยวกับ API ที่ดีไว้มาก จึงแนะนำให้อ่านควบคู่กันไป