2 คะแนน โดย GN⁺ 15 일 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • หากเว็บ API สาธารณะใช้ทั้งชื่ออย่าง Product API และพาธ /api/v1 ร่วมกัน อาจทำให้เวอร์ชันเชิงความหมายและโครงสร้างของ API ไม่สอดคล้องกัน
  • หากใช้พาธ /v1/ ควบคู่กับ major.minor.patch จะทำให้ route กับสัญญา API ปะปนกัน และเลขตัวแรกของ semantic version ถูกตรึงไว้ใน URL
  • การเปลี่ยนแปลงที่ทำให้ความเข้ากันได้พังจำเป็นต้องมีพาธใหม่และการทำ route ผ่าน reverse proxy ส่งผลให้ข้อมูลสัญญาถูกกระจายอยู่ทั้งใน URL และหมายเลขเวอร์ชัน
  • หากสร้าง API รุ่นถัดไปไปพร้อมกัน API เดิมก็แทบจะถูกผูกไว้กับ v1 โดยปริยาย และแม้ภายหลังจะมีการเปลี่ยนแปลงที่ทำให้ความเข้ากันได้พัง ความหมายของชื่อและพาธก็จะยิ่งคลุมเครือ
  • อยากทราบว่ามีจุดที่น่ารำคาญซ้ำ ๆ หรือมี หลักการออกแบบ ที่ดีกว่าสำหรับการจัดการเวอร์ชันของเว็บ API สาธารณะหรือไม่

1 ความคิดเห็น

 
GN⁺ 15 일 전
ความคิดเห็นจาก Lobste.rs
  • การใส่ /v1/ ไว้ใน URL จริง ๆ แล้วเป็นข้อดีอย่างหนึ่ง เพราะมัน บังคับไม่ให้ API พัง สำหรับผู้ใช้ จนกว่าจะปิด endpoint นั้น

  • บทความอื่นของผู้เขียนคนเดียวกันอย่าง Evolving HTTP APIs ให้คำแนะนำที่มีประโยชน์

  • โดยพื้นฐานแล้วจะใส่ /v1/, /v2/ ไว้ในแต่ละ route เพื่อบ่งบอกการเปลี่ยนแปลงแบบ breaking change หากเป็น public production API ไม่ใช่มาตรฐานที่พยายามนิยามให้ทำงานข้ามหลายโฮสต์ ก็แทบไม่มีเหตุผลต้องใช้ semantic versioning แบบเต็มรูปแบบ
    semantic versioning มีไว้เพื่อให้นักพัฒนาคนอื่นอัปเดต dependency ได้อย่างมั่นใจโดยไม่ต้องเสียเวลาอ่าน changelog ทีละ 20 นาที แต่ใน API ที่ใช้งานจริง คนใช้เลือกไม่ได้ว่าจะรับ minor version ใหม่หรือ bugfix version เมื่อไร
    สิ่งที่นับเป็น breaking change คือการเปลี่ยน พฤติกรรมที่มีการระบุไว้ในเอกสาร หรือทำให้ client เดิมที่พึ่งพาพฤติกรรมนั้นใช้งานไม่ได้ บางที่นับการเปลี่ยนพฤติกรรมที่ไม่ได้ระบุไว้ในเอกสารว่าเป็น breaking change ด้วย แต่แนวทางนั้นมีความเสี่ยงมาก

  • ที่ Google ใช้แนวทางนี้: AIP-185: API Versioning, AIP-180: Bacwards compatibility
    รู้สึกว่าเอกสารออกแบบพวกนี้ค่อนข้างเฉพาะกับวิธีทำงานของ Google แต่ก็เคยใช้อ้างอิงตอนออกแบบ API และมีไอเดียบางส่วนที่มีประโยชน์มาก

  • โดยทั่วไปคิดว่า API ทุกตัวควรพยายามลด breaking change ให้มากที่สุด ตัวอย่างเช่น ถ้าอยากเปลี่ยนชื่อ property ก็ควรเพิ่มชื่อใหม่ซ้ำเข้าไปมากกว่าลบ property เดิมทิ้ง
    แต่แนวทางแบบ the people at Buttondown do it ก็สะอาดดี คือกำหนด migration ระหว่างเวอร์ชันของ API เพื่อให้ผู้ใช้ตรึงเวอร์ชัน API ของตัวเองผ่าน header ได้ และฝั่งผู้ให้บริการก็เดินหน้าปรับเปลี่ยนต่อได้

    • วิธีซ้ำ property แบบนี้ใช้ได้ค่อนข้างดีสำหรับ output แต่กับ input จะต้องรับมือกรณีที่ client ส่งสอง property มาเป็นคนละค่า
      คำตอบที่นึกได้คือ “ให้ชื่อใหม่มีลำดับความสำคัญเสมอ” แต่พอ client ทำลำดับอ่าน-แก้ไข-เขียน แล้วส่งออบเจ็กต์ที่เซิร์ฟเวอร์สร้างกลับมาในเวอร์ชันแก้ไข วิธีนี้อาจพังได้ เพราะ client อาจอัปเดตแค่ property เก่า และส่ง property ใหม่ที่ไม่ได้แตะต้องกลับมาทั้งเดิม
    • การมี migration ระหว่างเวอร์ชัน API ตามที่ผู้ให้บริการ API อธิบายไว้ดูเป็นแนวคิดที่ดี แต่ การใช้ HTTP request header สำหรับการทำ versioning อาจก่อปัญหาได้
    • ลิงก์นั้นอธิบายวิธีจัดการ รูปแบบข้อมูล ได้ดีมาก แต่เรื่องนั้นเป็นแค่ส่วนหนึ่ง และก็สงสัยว่าเวลาพฤติกรรมของ API เปลี่ยนไปจริง ๆ จะจัดการอย่างไร
      ดูเหมือนการแปลงแบบนั้นอาจนำไปใช้กับการแมปพฤติกรรมได้ด้วย แต่ถ้าไม่ได้อ่านพลาด บทความนั้นไม่ได้กล่าวถึงส่วนนี้
  • ในอุดมคติควรใส่เวอร์ชันไว้ใน path และทำให้เวอร์ชันใหม่มีลักษณะเป็นการเพิ่มเติมจากเดิม จากนั้น API เวอร์ชันเก่าก็สามารถ reroute คำขอไปยัง API เวอร์ชันใหม่กว่า โดยทำการแปลง input/output ที่จำเป็น ผ่านไปหลายปี เมื่อไม่มีใครใช้เวอร์ชันเก่าแล้วก็ลบออกได้ และ path /v1/ ก็จะกลายเป็น error

  • เคยอ่านมานิดหน่อยเกี่ยวกับการทำ API versioning ผ่าน content negotiation ด้วย Accept header ถ้าใครเคยทำ API versioning แบบนั้นจริง ๆ ก็อยากรู้ประสบการณ์
    จากประสบการณ์ของผม/ฉัน การทำ versioning ราย resource หรือแบบ global น่าจะเป็นวิธีที่ตรงไปตรงมาที่สุด สำหรับการเลิกใช้งาน API การใช้ HTTP response header Deprecation (RFC 9745) และสุดท้ายให้ endpoint เก่าส่ง response อย่าง 410 Gone ดูเป็นแนวทางที่สมเหตุสมผลในการผลักให้ client ย้ายไปเวอร์ชันใหม่
    นอกจากนี้ก็อยากรู้จริง ๆ ว่ามีใครเคยสร้าง API ที่วิวัฒน์ต่อได้ บ้างไหม แบบที่รับคำขอเวอร์ชันเก่าแล้วแปลภายในให้เป็นคำขอของ API เวอร์ชันใหม่ ก่อนจะค่อย ๆ ลบเวอร์ชันเก่าทิ้งเมื่อ client ย้ายออกหรือเมื่อเวลาผ่านไป