ทีม API ส่วนใหญ่จะมองสัญญา (contract) เป็นเรื่องรองที่คิดทีหลัง พวกเขาเขียนโค้ดก่อน แล้วค่อยสร้างสเปก จากนั้นก็ปล่อยให้ทั้งสองส่วนแตกต่างกันไป การออกแบบ API แบบ Git-native จะพลิกกลับลำดับนั้น คุณจะปฏิบัติต่อสัญญา API เหมือนเป็นซอร์สโค้ด จัดการเวอร์ชันด้วย Git และตรวจสอบทุกการเปลี่ยนแปลงในลักษณะเดียวกับการตรวจสอบตรรกะของแอปพลิเคชัน
คู่มือนี้เกี่ยวกับหลักปฏิบัติ ไม่ใช่เครื่องมือชิ้นใดชิ้นหนึ่ง คุณจะได้เรียนรู้วิธีออกแบบสัญญาในสาขา (branches) ตรวจสอบในคำขอรวมโค้ด (pull requests) และเปลี่ยนสเปกที่คอมมิตแล้วให้เป็น mocks, tests และเอกสาร เป้าหมายคือเวิร์กโฟลว์ที่ประวัติ Git ของคุณ คือ ประวัติ API ของคุณ
หากคุณทราบอยู่แล้วว่าเครื่องมือแบบ Spec-First มีลักษณะอย่างไร และต้องการดูการสาธิตผลิตภัณฑ์ โปรดอ่านบทความเสริมเกี่ยวกับ เวิร์กโฟลว์ API แบบ Git-native บทความนี้จะเน้นไปที่การปฏิบัติ
“Git-native” หมายถึงอะไรสำหรับงาน API
Git-native หมายความว่าคำจำกัดความ API ของคุณอยู่ใน repository ของคุณในรูปแบบไฟล์ข้อความธรรมดา ไม่ได้อยู่ในฐานข้อมูลคลาวด์ที่เป็นกรรมสิทธิ์ หรืออยู่หลังการล็อกอินของผู้ขาย แต่เป็นไฟล์ .yaml หรือ .json ที่อยู่ข้างโค้ดของคุณ และถูกติดตามด้วยระบบควบคุมเวอร์ชันเดียวกับที่ทีมของคุณใช้อยู่แล้ว
ลองเปรียบเทียบกับเครื่องมือที่ถูกล็อคกับคลาวด์ แพลตฟอร์มการออกแบบ API จำนวนมากจัดเก็บสัญญาไว้ในแบ็กเอนด์ของตนเอง คุณแก้ไขผ่าน UI บนเว็บ และเวอร์ชันหลักจะอยู่บนเซิร์ฟเวอร์ของพวกเขา Repository ของคุณจะเก็บได้เพียงข้อมูลที่ส่งออกไปแล้วซึ่งอาจล้าสมัยได้ หากฐานข้อมูลของผู้ขายเป็นแหล่งข้อมูลที่แท้จริง ประวัติ Git ของคุณจะไม่สามารถบอกอะไรเกี่ยวกับการพัฒนา API ได้เลย

โมเดล Git-native จะพลิกกลับความสัมพันธ์นั้น ไฟล์ใน main คือสัญญา ทุกสิ่งทุกอย่าง รวมถึง GUI ใด ๆ ก็ตาม เป็นเพียงมุมมองที่มองไปยังไฟล์นั้น การเปลี่ยนแปลงเพียงครั้งเดียวนี้จะปลดล็อกประวัติการเปลี่ยนแปลง ผู้รับผิดชอบ การย้อนกลับ และการตรวจสอบสำหรับพื้นผิว API ของคุณ
การตั้งค่า Git-native มีคุณสมบัติสามประการ สเปกเป็นไฟล์ข้อความใน repo การเปลี่ยนแปลงจะไหลผ่านการดำเนินการ Git ปกติ เช่น branch, commit และ merge และ artifacts ปลายน้ำทั้งหมด ตั้งแต่ mocks ไปจนถึง docs จะมาจากไฟล์ที่คอมมิตแล้ว ไม่ใช่จากฐานข้อมูลแยกต่างหาก
ทำไมต้องออกแบบและพัฒนา API ใน Git
คุณไว้วางใจ Git กับทรัพย์สินที่มีค่าที่สุดของคุณอยู่แล้ว นั่นคือโค้ดของคุณ สัญญา API ของคุณสมควรได้รับการปฏิบัติเช่นเดียวกัน
ประวัติการเปลี่ยนแปลงคือเหตุผลแรก เมื่อมีคนถามว่า “เราเพิ่มพารามิเตอร์การแบ่งหน้า cursor เข้าไปเมื่อไหร่” git log สามารถตอบได้ในไม่กี่วินาที commit ที่แนะนำมันจะมาพร้อมกับข้อความ ผู้เขียน และวันที่ ไม่ต้องมีภาพหน้าจอ ไม่ต้องขุดคุ้ย changelog
การระบุผู้รับผิดชอบ (Blame) เป็นเหตุผลที่สอง รัน git blame บนไฟล์สเปก แล้วคุณจะเห็นว่าใครเปลี่ยนแต่ละบรรทัดและทำไม ชื่อฟิลด์ที่สับสนสามารถย้อนกลับไปถึง PR ที่เพิ่มเข้ามาได้ พร้อมกับการสนทนาที่แนบมาด้วย ความรับผิดชอบจะเกิดขึ้นโดยอัตโนมัติ
การย้อนกลับ (Rollback) เป็นเหตุผลที่สาม ดีไซน์ที่ไม่ดีถูกนำไปใช้ ด้วย Git คุณสามารถ git revert การรวมโค้ดและสัญญาจะกลับสู่สถานะเดิม codegen, mocks และ docs ปลายน้ำจะถูกสร้างขึ้นใหม่จากไฟล์ที่ย้อนกลับ ไม่ต้องมีการทำความสะอาดด้วยตนเองในระบบอื่น
การตรวจสอบ (Review) เป็นเหตุผลที่สี่ และเป็นสิ่งที่ทีมใช้งานน้อยเกินไป pull request เป็นที่ที่เหมาะสมที่สุดสำหรับการถกเถียงการออกแบบ API ก่อน ที่มันจะกลายเป็นจริง ผู้ตรวจสอบจะแสดงความคิดเห็นบนบรรทัด + ที่เพิ่มฟิลด์ที่จำเป็น การสนทนาจะอยู่ข้างการเปลี่ยนแปลงตลอดไป
แหล่งความจริงแหล่งเดียว (Single source of truth) เชื่อมโยงทุกสิ่งเข้าด้วยกัน เมื่อสัญญาเป็นไฟล์เดียวใน main จะไม่มีความคลุมเครือว่าเวอร์ชันใดเป็นของจริง Frontend, backend, QA และเอกสารทั้งหมดจะอ่าน YAML บรรทัดเดียวกัน นี่คือคำมั่นสัญญาหลักของ เวิร์กโฟลว์การกำหนด API ด้วย Git
วงจรการออกแบบ API แบบ Git-native
วงจรนี้มีห้าขั้นตอน: ออกแบบสัญญา, คอมมิต, เปิด PR, ตรวจสอบ และรวมโค้ด การนำไปใช้งานจะตามมาหลังจากรวมโค้ดแล้ว ไม่ใช่ในทางกลับกัน
เริ่มต้นด้วยการเขียนสัญญา สมมติว่าคุณกำลังเพิ่ม endpoint เพื่อดึงใบแจ้งหนี้ของผู้ใช้ คุณสร้าง branch และแก้ไขไฟล์ OpenAPI
# openapi.yaml (excerpt added on branch feat/invoices-list)
paths:
/users/{userId}/invoices:
get:
operationId: listUserInvoices
summary: List invoices for a user
parameters:
- name: userId
in: path
required: true
schema: { type: string, format: uuid }
- name: status
in: query
required: false
schema:
type: string
enum: [draft, open, paid, void]
responses:
"200":
description: A page of invoices
content:
application/json:
schema:
$ref: "#/components/schemas/InvoiceList"
"404":
description: User not found
คอมมิตการเปลี่ยนแปลงนั้นด้วยข้อความที่ชัดเจน: git commit -m "Add GET /users/{userId}/invoices contract" การคอมมิตมีขนาดเล็กและเน้นเฉพาะจุด อธิบายการตัดสินใจออกแบบเพียงอย่างเดียว
ตอนนี้เปิด pull request การเปรียบเทียบ (diff) จะแสดงให้ผู้ตรวจสอบเห็นว่ามีอะไรใหม่: หนึ่ง path, หนึ่ง operation, สองพารามิเตอร์, สอง responses เพื่อนร่วมทีมของคุณจะหารือเกี่ยวกับการตั้งชื่อ ค่า enum และว่ารหัส 404 เหมาะสมหรือไม่สำหรับผู้ใช้ที่ไม่มีอยู่จริง พวกเขาอาจผลักดันให้ใช้การแบ่งหน้าแบบ cursor ก่อนที่จะมีโค้ด handler แม้แต่บรรทัดเดียว
เมื่อ PR ได้รับการอนุมัติ ก็รวมโค้ดได้เลย สัญญาใน main ตอนนี้รวมถึง endpoint สำหรับใบแจ้งหนี้แล้ว การนำไปใช้งานจะตามมา และถูกจำกัดด้วยสเปกที่คุณทุกคนตกลงกันไว้ ลำดับนี้คือสิ่งที่ผู้คนหมายถึง การพัฒนา API แบบ spec-first: ข้อตกลงมาก่อนโค้ด
ผลตอบแทนคือการถกเถียงเรื่องการออกแบบทำได้ในราคาถูก การเปลี่ยนฟิลด์ YAML ในการตรวจสอบใช้เวลาเพียงไม่กี่นาที การเปลี่ยน endpoint ที่ถูกนำไปใช้จริง พัฒนาแล้ว และมีเอกสารประกอบ จะใช้เวลาเป็นวัน
กลยุทธ์การแตกสาขาสำหรับสัญญา API
ปฏิบัติต่อการเปลี่ยนแปลงสัญญาเหมือนกับการเปลี่ยนแปลงอื่น ๆ: หนึ่ง branch ต่อหนึ่งหน่วยงานของงานเชิงตรรกะ หนึ่ง branch ต่อหนึ่ง endpoint หรือต่อหนึ่งการแก้ไข จะทำให้ PR มีขนาดเล็กและอ่าน diff ได้ง่าย
ตั้งชื่อ branch ให้ความตั้งใจชัดเจน ใช้คำนำหน้าที่บ่งบอกประเภทของการเปลี่ยนแปลง สิ่งนี้ช่วยให้ผู้ตรวจสอบและ CI จัดการงานได้
| ประเภทการเปลี่ยนแปลง | คำนำหน้า Branch | ตัวอย่าง | น้ำหนักการตรวจสอบ |
|---|---|---|---|
| Endpoint ใหม่ | feat/api- |
feat/api-invoices-list |
มาตรฐาน |
| ฟิลด์ที่เพิ่มเข้ามา | feat/api- |
feat/api-invoice-currency |
เบา |
| การเปลี่ยนแปลงที่ส่งผลกระทบ | break/api- |
break/api-remove-legacy-id |
หนัก, ต้องการการอนุมัติ |
| แก้ไขข้อผิดพลาดในสเปก | fix/api- |
fix/api-status-enum-typo |
เบา |
| ปรับโครงสร้างเท่านั้น | chore/api- |
chore/api-reorder-schemas |
เบา |
คำนำหน้ามีความหมาย branch ที่ขึ้นต้นด้วย break/api- จะบอกให้ผู้ตรวจสอบชะลอความเร็วและตรวจสอบผู้ใช้งาน (consumer) ทุกราย ส่วน branch ที่ขึ้นต้นด้วย chore/api- จะบ่งชี้ว่าไม่มีการเปลี่ยนแปลงเชิงความหมาย ดังนั้นการตรวจสอบจึงสามารถดำเนินการได้อย่างรวดเร็ว
คุณยังสามารถเลือกรุ่นการแตกสาขา (branching model) ได้อีกด้วย การพัฒนาแบบ Trunk-based เหมาะกับทีม API ส่วนใหญ่ สาขาที่มีอายุสั้นจะถูกรวมเข้ากับ main ทุกวัน และสเปกจะอยู่ใกล้เคียงกับแหล่งความจริงเพียงแหล่งเดียว ส่วน Gitflow ที่มี branch develop และ release ที่ทำงานเป็นเวลานาน เหมาะกับทีมที่รวมการเปลี่ยนแปลง API เป็นชุดในการเผยแพร่ตามกำหนดเวลา
| โมเดล | เหมาะสำหรับ | ข้อแลกเปลี่ยนของ API |
|---|---|---|
| Trunk-based | การส่งมอบต่อเนื่อง, ทีมขนาดเล็ก | สัญญาพัฒนาทีละเล็กละน้อย; ลดปัญหาการรวมโค้ด |
| Gitflow | การเผยแพร่ตามกำหนดเวลา, การจัดส่งที่มีการควบคุม | สเปกแตกต่างกันบน develop; การรวมโค้ดขนาดใหญ่และมีความเสี่ยงสูงกว่า |
สำหรับงาน API ส่วนใหญ่ ควรเลือกแบบ Trunk-based การเปลี่ยนแปลงสัญญาที่เล็กและบ่อยครั้งจะสร้าง diff ที่เล็กและบ่อยครั้งเช่นกัน สาขาที่มีอายุยืนยาวทำให้สเปกเกิดความคลาดเคลื่อนได้ และข้อขัดแย้งในการรวม YAML ก็จะเกิดขึ้นอย่างรวดเร็วเมื่อสอง branch ปรับโครงสร้างไฟล์เดียวกัน
การตรวจสอบการออกแบบ API ใน pull requests
PR ของสเปกคือการตรวจสอบการออกแบบ ไม่ใช่การตรวจสอบไวยากรณ์ ผู้ตรวจสอบจะดูความหมาย และคำถามบางข้อก็ครอบคลุมความเสี่ยงส่วนใหญ่
สิ่งนี้จะทำให้ผู้ใช้งานปัจจุบันใช้งานไม่ได้หรือไม่? การลบฟิลด์, การเปลี่ยนชื่อ path หรือการจำกัดประเภท ล้วนเป็นการเปลี่ยนแปลงที่ส่งผลกระทบ ผู้ตรวจสอบจะตรวจสอบว่าการเปลี่ยนแปลงนั้นเป็นการเพิ่มเข้ามาหรือเป็นการเปลี่ยนแปลงที่ส่งผลกระทบ และการเปลี่ยนแปลงที่ส่งผลกระทบจะต้องมีการเพิ่มเวอร์ชันหรือเส้นทางการเลิกใช้งาน
การตั้งชื่อสอดคล้องกันหรือไม่? หาก endpoint ของคอลเลกชันทั้งหมดใช้คำนามพหูพจน์ path ใหม่ที่เป็นเอกพจน์จะโดดเด่น หากข้อผิดพลาดส่งคืนฟิลด์ code ที่อื่น endpoint ใหม่ก็ควรทำเช่นกัน ผู้ตรวจสอบจะบังคับใช้รูปแบบที่ API ได้กำหนดไว้แล้ว
เป็นมิตรกับการเปรียบเทียบ (diff-friendly) หรือไม่? ทำให้ YAML มีความเสถียรเพื่อให้ diff มีขนาดเล็ก จัดลำดับ key ให้สอดคล้องกัน เพิ่ม path ใหม่ในตำแหน่งที่คาดเดาได้ ผู้ตรวจสอบสามารถอ่าน diff ห้าบรรทัดได้ในไม่กี่วินาที แต่ไฟล์ที่จัดเรียงใหม่จะสร้าง diff เป็นร้อยบรรทัดที่ซ่อนการเปลี่ยนแปลงที่แท้จริง
การเปลี่ยนแปลงที่ส่งผลกระทบที่เป็นมิตรต่อผู้ตรวจสอบ บรรทัดที่มีเครื่องหมาย - จะระบุอย่างชัดเจนว่ามีอะไรหายไป
# Diff a reviewer sees in the PR
parameters:
- name: status
in: query
schema:
type: string
- enum: [draft, open, paid, void]
+ enum: [draft, open, paid, void, uncollectible]
diff นั้นเป็นการเพิ่มเข้าไปใน enum ดังนั้นจึงปลอดภัย ลองเปรียบเทียบกับบรรทัด - ที่ลบ void ออกทั้งหมด ซึ่งจะทำให้ไคลเอ็นต์ใด ๆ ที่ส่งค่านั้นเกิดข้อผิดพลาดได้ diff ทำให้ความแตกต่างมองเห็นได้ทันที
กระตุ้นให้ผู้ตรวจสอบแสดงความคิดเห็นแบบอินไลน์บนสเปก เช่นเดียวกับการแสดงความคิดเห็นบนโค้ด การสนทนาจะยังคงติดอยู่กับบรรทัดนั้นและคงอยู่ตลอดไปในประวัติการรวมโค้ด
จากการออกแบบสู่การพัฒนา
เมื่อสัญญาอยู่ใน main แล้ว มันจะกลายเป็นแหล่งที่มาสำหรับทุกสิ่งปลายน้ำ คุณสร้างขึ้นมา ไม่ได้เขียนด้วยมือ

Codegen มาเป็นอันดับแรก เครื่องมืออย่าง openapi-generator สร้าง server stubs และ typed clients จากไฟล์ที่คอมมิตแล้ว ตัวจัดการ (handlers) ของคุณจะเติมตรรกะทางธุรกิจ แต่รูปร่างของคำขอและคำตอบจะตรงกับสัญญาโดยการสร้าง สเปกและโค้ดไม่สามารถขัดแย้งกันเกี่ยวกับรูปแบบการส่งข้อมูลได้
Mocks มาเป็นลำดับถัดไป เซิร์ฟเวอร์ mock จะอ่านสเปกและส่งคืนตัวอย่างการตอบสนองสำหรับทุก endpoint นักพัฒนา Frontend สร้างโดยใช้ mock ก่อนที่ backend จะมีอยู่จริง พวกเขาเริ่มต้นทันทีที่สัญญารวมโค้ด ไม่ใช่หลายสัปดาห์ต่อมา
ตามมาด้วยการทดสอบ (Tests) Contract tests ยืนยันว่าเซิร์ฟเวอร์ที่กำลังทำงานของคุณตรงกับสเปก ส่งคำขอ ตรวจสอบการตอบสนองกับ schema และทำให้ build ล้มเหลวหากแตกต่างกัน นี่คือแนวป้องกันความคลาดเคลื่อนของ spec/code
เอกสารก็สร้างขึ้นเช่นกัน เอกสารอ้างอิงสร้างขึ้นโดยตรงจากไฟล์ OpenAPI เมื่อสัญญาเปลี่ยนแปลง เอกสารก็จะเปลี่ยนแปลงในการคอมมิตเดียวกัน ไม่มีการอัปเดตเอกสารแยกต่างหากที่อาจถูกลืม
หลักการมีความสอดคล้องกัน ทุก artifact มาจากไฟล์ที่คอมมิตแล้วไฟล์เดียว สร้างขึ้นใหม่ทุกครั้งที่รวมโค้ด และ mocks, clients, tests และเอกสารของคุณจะสอดคล้องกับสัญญา
แนวปฏิบัติของทีมที่ปรับขนาดได้
แนวปฏิบัติคือสิ่งที่ช่วยให้เวิร์กโฟลว์แบบ Git-native ไม่ล่มสลายเมื่อทีมเติบโต ตัดสินใจแต่เนิ่นๆ และจดบันทึกไว้
อันดับแรก เลือกระหว่างไฟล์สเปกเดียวกับหลายไฟล์ ไฟล์ openapi.yaml ไฟล์เดียวทำได้ง่ายแต่จะจัดการยากเมื่อมี endpoint เกินสองสามสิบไฟล์ การแยกเป็นหลายไฟล์ด้วยการอ้างอิง $ref จะทำให้แต่ละไฟล์อ่านง่ายขึ้น แต่ต้องมีขั้นตอนการรวมไฟล์ รูปแบบทั่วไปคือหนึ่งไฟล์ต่อหนึ่งทรัพยากร ซึ่งจะถูกรวมเป็นสเปกเดียวในขั้นตอนการ build
ประการที่สอง กำหนดเวอร์ชันอย่างรอบคอบ เพิ่ม info.version ของ OpenAPI ทุกครั้งที่มีการเปลี่ยนแปลงที่มีความหมาย และปฏิบัติตาม semantic versioning การเปลี่ยนแปลงแบบเพิ่มจะเพิ่มเวอร์ชันย่อย การเปลี่ยนแปลงที่ส่งผลกระทบจะเพิ่มเวอร์ชันหลัก และโดยปกติหมายถึงคำนำหน้า path ใหม่ เช่น /v2/
ประการที่สาม เก็บ changelog ไฟล์ CHANGELOG.md ที่อยู่ข้างสเปกจะบันทึกว่ามีการเปลี่ยนแปลงอะไรบ้างและทำไมในภาษาที่มนุษย์เข้าใจได้ ประวัติ Git นั้นแม่นยำแต่มีรายละเอียดมากเกินไป changelog คือบทสรุปที่อ่านง่ายที่ผู้ใช้งานของคุณอ่านจริง ๆ
ประการที่สี่ ป้องกันสเปกด้วย CODEOWNERS กำหนดให้ API stewards อนุมัติการเปลี่ยนแปลงใด ๆ ในไฟล์สัญญา สิ่งนี้จะหยุดผู้มีส่วนร่วมที่มีเจตนาดีจากการส่งมอบการออกแบบที่ไม่สอดคล้องกัน
# .github/CODEOWNERS
/api/openapi.yaml @api-stewards
/api/paths/ @api-stewards
ประการที่ห้า ทำ lint ใน CI ตัว linter จะจับปัญหาด้านสไตล์และความสอดคล้องก่อนการตรวจสอบ รันมันในทุก PR เพื่อให้คนตรวจสอบการออกแบบ ไม่ใช่การจัดรูปแบบ
# .github/workflows/api-lint.yml
name: API Lint
on:
pull_request:
paths: ["api/"]
jobs:
spectral:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Spectral
run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
ด้วย CI linting และ CODEOWNERS การเปลี่ยนแปลงสัญญาแต่ละครั้งจะได้รับการตรวจสอบโดยอัตโนมัติและมีผู้ดูแลที่เป็นคน การรวมกันนี้สามารถปรับขนาดได้ตั้งแต่สามวิศวกรไปจนถึงสามร้อยคน
ข้อผิดพลาดทั่วไปและวิธีหลีกเลี่ยง
การออกแบบ API แบบ Git-native มีรูปแบบความล้มเหลวที่คาดเดาได้ การรู้สิ่งเหล่านี้ช่วยให้คุณออกแบบเพื่อหลีกเลี่ยงได้
ความคลาดเคลื่อนระหว่างสเปก/โค้ดเป็นสิ่งที่เลวร้ายที่สุด สัญญากล่าวอย่างหนึ่ง แต่เซิร์ฟเวอร์ที่กำลังทำงานทำอีกอย่างหนึ่ง ป้องกันได้ด้วย contract tests ใน CI ที่ตรวจสอบการตอบสนองจริงกับสเปกที่คอมมิต หากพวกมันแตกต่างกัน build จะล้มเหลว ความคลาดเคลื่อนกลายเป็น pipeline ที่พัง ไม่ใช่ความประหลาดใจในการผลิต
PR ขนาดใหญ่คือกับดักถัดไป branch เดียวที่เพิ่มยี่สิบ endpoint จะสร้าง diff ที่ไม่สามารถตรวจสอบได้ ผู้ตรวจสอบจะอ่านผ่านๆ อนุมัติ และมองข้ามปัญหาไป แยกงานออกเป็นหนึ่ง endpoint หรือหนึ่งการเปลี่ยนแปลงต่อ PR diff ขนาดเล็กจะได้รับการตรวจสอบจริง
Artifacts ที่เขียนด้วยมือทำให้เกิดความไม่สอดคล้องกันโดยไม่รู้ตัว เมื่อมีคนเขียนไคลเอ็นต์ด้วยมือแทนที่จะสร้างขึ้น ไคลเอ็นต์นั้นจะคลาดเคลื่อนจากสเปก สร้าง clients, stubs และ docs จากไฟล์ที่คอมมิตแล้วทุกครั้ง ถือว่า API artifacts ที่เขียนด้วยมือเป็นสัญญาณที่ไม่ดี
ข้อขัดแย้งในการรวม YAML ทำให้ทีมที่ใช้ branch ที่มีอายุยืนยาวรู้สึกหงุดหงิด สอง branch ปรับโครงสร้างไฟล์เดียวกันและ Git ไม่สามารถแก้ไขความขัดแย้งได้ หลีกเลี่ยงปัญหานี้ด้วย branch ที่มีอายุสั้น ลำดับ key ที่เสถียร และโครงสร้างไฟล์แบบแยกส่วน เพื่อให้การเปลี่ยนแปลงกระทบไฟล์ที่แตกต่างกัน การพัฒนาแบบ Trunk-based บวกกับ PR ขนาดเล็กช่วยขจัดความขัดแย้งส่วนใหญ่ก่อนที่จะเริ่มต้น
รูปแบบในทั้งสี่ประการเหมือนกัน รักษาการเปลี่ยนแปลงให้เล็ก สร้าง artifacts จากสเปก และให้ CI บังคับใช้สัญญา วินัยดีกว่าความกล้าหาญ
Apidog เข้ามามีบทบาทอย่างไร
คุณสามารถใช้งานเวิร์กโฟลว์แบบ Git-native ได้ด้วย Text Editor และ CLI หลายทีมต้องการ GUI สำหรับการออกแบบโดยไม่ละทิ้ง Git ในฐานะแหล่งความจริง นั่นคือช่องว่างที่ Apidog’s Spec-First Mode เข้ามาเติมเต็ม
Spec-First Mode จะเก็บไฟล์ OpenAPI ไว้ใน Git repository ของคุณและมีการซิงค์แบบสองทาง คุณแก้ไขสัญญาใน Apidog’s visual designer หรือใน editor ของคุณ และทั้งสองส่วนจะยังคงสอดคล้องกับไฟล์ใน repo ของคุณ ไฟล์ใน Git ยังคงเป็นหลักการสำคัญ ดังนั้น branches, PRs และประวัติการเปลี่ยนแปลงทั้งหมดจะทำงานได้ตามที่อธิบายไว้ที่นี่ คุณจะได้รับ GUI โดยไม่มีข้อจำกัดด้านคลาวด์ ดู เอกสาร Spec-First Mode สำหรับรายละเอียดการตั้งค่า
ประเด็นไม่ใช่เครื่องมือ แต่คือคุณสามารถมีหน้าจอการออกแบบภาพ (visual design surface) และหลักปฏิบัติแบบ Git-native ได้ในเวลาเดียวกัน Repo ยังคงเป็นแหล่งความจริงเพียงแหล่งเดียว และ Apidog กลายเป็นมุมมองหนึ่งที่มองไปยังสิ่งนั้น
คำถามที่พบบ่อย
การออกแบบ API แบบ Git-native ใช้ได้กับ OpenAPI เท่านั้นหรือไม่?
ไม่ หลักปฏิบัตินี้ใช้ได้กับรูปแบบสัญญาที่เป็นข้อความใด ๆ ก็ตาม OpenAPI เป็นรูปแบบที่พบได้บ่อยที่สุด แต่เวิร์กโฟลว์เดียวกันนี้ก็ใช้ได้กับ AsyncAPI, ไฟล์ .proto ของ gRPC หรือ GraphQL SDL ตราบใดที่สัญญานั้นเป็นไฟล์ข้อความที่คุณสามารถเปรียบเทียบ (diff), แตกสาขา (branch) และตรวจสอบ (review) ได้ มันก็คือ Git-native
ฉันจะจัดการกับการเปลี่ยนแปลงที่ส่งผลกระทบในเวิร์กโฟลว์แบบ Git-native ได้อย่างไร?
ทำให้การเปลี่ยนแปลงที่ส่งผลกระทบมองเห็นได้และจงใจ ใช้คำนำหน้า branch break/api- เพิ่มเวอร์ชันหลัก และกำหนดให้ผู้ดูแล (steward) ลงนามอนุมัติผ่าน CODEOWNERS หากเป็นไปได้ ให้เพิ่มรูปแบบใหม่ควบคู่ไปกับรูปแบบเก่า และเลิกใช้งาน path เก่าตามกำหนดเวลา PR diff และการเพิ่มเวอร์ชันจะส่งสัญญาณการเปลี่ยนแปลงที่ส่งผลกระทบไปยังผู้ใช้งานทุกคน
สเปก API ควรอยู่ใน repo เดียวกับโค้ดหรือไม่?
โดยปกติแล้วใช่ เมื่อทีมเดียวเป็นเจ้าของทั้งสองส่วน การจัดวางสเปกและการนำไปใช้งานไว้ด้วยกันหมายความว่า PR เดียวสามารถเปลี่ยนแปลงสัญญาและตัวจัดการได้พร้อมกัน และ contract tests จะทำงานใน pipeline เดียวกัน ใส่สเปกใน repo แยกต่างหากเมื่อมีหลายทีมใช้งาน API ที่ใช้ร่วมกันเพียงหนึ่งเดียวและต้องการการกำหนดเวอร์ชันที่เป็นอิสระเท่านั้น
ฉันจะป้องกันไม่ให้สเปกและโค้ดคลาดเคลื่อนกันได้อย่างไร?
เพิ่ม contract tests เข้าไปใน CI พวกมันจะส่งคำขอจริงไปยังเซิร์ฟเวอร์ที่กำลังทำงานของคุณและตรวจสอบการตอบสนองทุกรายการกับสเปกที่คอมมิตไว้ ความแตกต่างจะทำให้ build ล้มเหลว เมื่อรวมกับการสร้าง stubs และ clients จากสเปกแล้ว contract tests จะทำให้ความคลาดเคลื่อนกลายเป็นความล้มเหลวของ pipeline แทนที่จะเป็นข้อผิดพลาดในการผลิต
บทสรุป
การออกแบบ API แบบ Git-native คือหลักปฏิบัติ ไม่ใช่ผลิตภัณฑ์ คุณปฏิบัติต่อสัญญาเหมือนเป็นซอร์สโค้ด พัฒนาใน branch ตรวจสอบใน pull requests และสร้าง artifacts ปลายน้ำทุกรายการจากไฟล์ที่คอมมิตแล้ว ประวัติการเปลี่ยนแปลง ผู้รับผิดชอบ การย้อนกลับ และการตรวจสอบ คุณได้รับสิ่งเหล่านี้ฟรี เพราะ Git มอบให้คุณอยู่แล้ว
เริ่มต้นจากเล็ก ๆ ย้ายสเปกของคุณเข้าไปใน repo เพิ่มขั้นตอน linting และกำหนดให้มีการตรวจสอบการเปลี่ยนแปลงสัญญา สร้างต่อจากนั้นด้วย codegen, mocks และ contract tests เวิร์กโฟลว์จะเสริมกัน: แต่ละแนวปฏิบัติจะทำให้ขั้นตอนถัดไปง่ายขึ้น และประวัติ Git ของคุณจะกลายเป็นบันทึกที่สมบูรณ์ว่า API ของคุณเติบโตขึ้นอย่างไร
หากคุณต้องการหน้าจอการออกแบบภาพที่เก็บสเปกไว้ใน Git ลองใช้ Spec-First Mode ใน Apidog และดูว่าการซิงค์แบบสองทางเข้ากับเวิร์กโฟลว์ข้างต้นได้อย่างไร
