สัญญา API ของคุณอาจกระจายอยู่หลายที่พร้อมกัน: แผนภาพในวิกิ, คอลเลกชัน Postman ที่ export ไว้เมื่อไตรมาสก่อน, หรือเอกสาร Markdown ที่ไม่ตรงกับบริการจริงแล้ว เมื่อแหล่งข้อมูลเหล่านี้ไม่ตรงกัน ทีมจะเริ่มเดา และการเดาคือจุดเริ่มต้นของ integration ที่พัง
วิธีแก้คือจัดการข้อกำหนด API ให้เป็นโค้ด: เขียน OpenAPI เป็นไฟล์เดียว, commit เข้า Git, review ผ่าน pull request และใช้ไฟล์นั้นสร้าง mocks, tests, docs และ SDKs ต่อไป ข้อกำหนดจึงไม่ใช่เอกสารประกอบที่ทำทีหลัง แต่เป็นสัญญาหลักที่ทุกคนใช้ร่วมกัน
บทความนี้สรุปวิธีเริ่มทำ spec-as-code แบบลงมือได้จริง: เก็บ OpenAPI ไว้ตรงไหน, review อย่างไร, ต่อกับ CI อย่างไร และใช้เครื่องมืออย่าง Apidog โดยไม่ทำให้ Git workflow ของทีมเสียรูป
Spec-as-code หมายถึงอะไร
Spec-as-code คือการเก็บคำจำกัดความ API เป็นไฟล์ข้อความธรรมดา เช่น openapi.yaml หรือ openapi.json ภายใต้ version control ไม่ใช่ข้อมูลที่อยู่เฉพาะในเครื่องมือใดเครื่องมือหนึ่ง และไม่ใช่เอกสารที่แก้ไขแยกจากโค้ด
เมื่อ spec เป็นไฟล์ใน Git คุณสามารถทำสิ่งเหล่านี้ได้เหมือนซอร์สโค้ด:
git diff api/openapi.yaml
git checkout -b update-orders-api
git commit -m "Add shippedAt to Order schema"
git revert <commit>
แนวคิดนี้ต่อยอดจาก docs-as-code โดยตรง เอกสารประกอบอยู่ใน repo เดียวกับโค้ดและถูกส่งผ่าน pipeline เดียวกัน ส่วน spec-as-code ใช้แนวทางเดียวกันกับสัญญา API: ไฟล์ OpenAPI คือ artifact หลัก และผลลัพธ์อื่น ๆ เช่น mock server, test case, API docs และ SDK จะถูกสร้างจากไฟล์นี้
ผลลัพธ์คือทีมมีแหล่งความจริงเพียงแหล่งเดียว ถ้านักพัฒนาต้องการรู้ว่า GET /users/{id} ส่งคืนอะไร ให้ดูที่ OpenAPI spec ไม่ใช่วิกิที่อาจล้าสมัย ถ้า QA ต้องเขียน test ให้ validate กับ spec ถ้าพาร์ทเนอร์ต้อง integrate ให้ใช้ SDK หรือ docs ที่สร้างจาก spec เดียวกัน
ทำไมต้องจัดการข้อกำหนดเหมือนโค้ด
เมื่อข้อกำหนด API อยู่ใน Git คุณจะได้ workflow ที่ทีม dev คุ้นเคยอยู่แล้ว
1. Review การเปลี่ยนแปลงผ่าน Pull Request
การเปลี่ยนแปลง endpoint จะแสดงเป็น diff ใน PR ผู้ review เห็นได้ทันทีว่า:
- path ใหม่ถูกเพิ่มหรือไม่
- field ใดถูกเพิ่ม ลบ หรือเปลี่ยน type
- status code ใดเปลี่ยนไป
- response schema ยัง backward compatible หรือไม่
ตัวอย่าง diff ที่ควรถูก review อย่างจริงจัง:
status:
type: string
- enum: [pending, shipped, delivered]
+ enum: [pending, shipped, delivered, cancelled]
การเพิ่ม enum อาจปลอดภัยสำหรับบาง client แต่ก็อาจทำให้ client ที่ handle ค่าแบบ exhaustive พังได้ การจับเรื่องนี้ใน PR ดีกว่าปล่อยให้เจอใน production
นี่คือหัวใจของ เวิร์กโฟลว์ API ที่ทำงานร่วมกับ Git
2. Diff อ่านง่ายและตรวจสอบได้
YAML หรือ JSON ที่จัดรูปแบบดีจะ diff ได้ชัดเจน คุณสามารถดูการเปลี่ยนแปลงไม่กี่บรรทัดแล้วเข้าใจได้ทันที ต่างจากไฟล์ export หรือข้อมูลที่อยู่เฉพาะใน hosted tool ซึ่งมักตอบคำถาม “เปลี่ยนอะไรไปบ้าง” ได้ยาก
3. มี version control จริง
ทุกการเปลี่ยนแปลงมี commit, author และ timestamp คุณสามารถ:
git tag api-v1.2.0
git checkout -b design-api-v2
git revert <bad-spec-change>
ประวัติ API จึง audit ได้ และ rollback ได้ด้วยเครื่องมือเดียวกับที่ใช้กับโค้ด อ่านเพิ่มเติมเรื่องกลยุทธ์ branch/tag ได้ที่ การควบคุมเวอร์ชัน OpenAPI ด้วย Git
4. สร้าง mocks, tests, docs และ SDK จากแหล่งเดียว
เมื่อทุกอย่างสร้างจากไฟล์เดียวกัน ความคลาดเคลื่อนจะลดลง:
- เปลี่ยน spec
- run generator หรือ pipeline
- mocks/docs/tests/SDKs อัปเดตตาม
ตารางเปรียบเทียบ:
| ข้อกังวล | ข้อกำหนดในเครื่องมือที่โฮสต์ | ข้อกำหนด API เป็นโค้ด |
|---|---|---|
| การตรวจสอบการเปลี่ยนแปลง | แบบแมนนวล, ตรวจสอบพลาดได้ง่าย | PR diff, การตรวจสอบที่บังคับ |
| ประวัติ | จำกัด หรือ ถูกผูกขาดโดยผู้ขาย | บันทึก Git แบบสมบูรณ์ |
| การย้อนกลับ | ส่วนใหญ่มักทำด้วยตนเอง | git revert |
| แหล่งความจริง | คลุมเครือ | ไฟล์ที่คอมมิต |
| การเชื่อมต่อ CI | เพิ่มเข้ามาทีหลัง | ในตัว |
OpenAPI ในฐานะ Artifact หลัก
OpenAPI เหมาะกับ spec-as-code เพราะเป็นมาตรฐานที่รองรับกว้าง อ่านได้ทั้งโดยคนและเครื่องมือ ตัวอย่างไฟล์ OpenAPI 3.1 แบบย่อ:
openapi: 3.1.0
info:
title: Orders API
version: 1.2.0
paths:
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrder
parameters:
- name: orderId
in: path
required: true
schema:
type: string
format: uuid
responses:
"200":
description: The requested order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
"404":
description: Order not found
components:
schemas:
Order:
type: object
required: [id, status, total]
properties:
id:
type: string
format: uuid
status:
type: string
enum: [pending, shipped, delivered]
total:
type: number
format: float
โครงสร้าง repo ที่เรียบง่ายอาจเป็นแบบนี้:
my-service/
├── api/
│ └── openapi.yaml
├── src/
├── tests/
└── package.json
ไฟล์ api/openapi.yaml คือสัญญา ถ้าต้องเพิ่ม field ใน Order ให้แก้ที่ spec ก่อนหรือพร้อมกับ implementation แล้วเปิด PR เพื่อให้ทีม review ทั้งสองส่วนร่วมกัน
ข้อกำหนดและเอกสาร: ใช้ไฟล์เดียวสร้างหลายผลลัพธ์
เมื่อมี OpenAPI เป็นแหล่งต้นทางเดียว คุณสามารถสร้าง output ที่จำเป็นต่อ workflow ได้หลายแบบ
Mocks
ชี้ mock server ไปที่ OpenAPI spec แล้ว frontend หรือ mobile team สามารถเริ่ม integrate ได้ก่อน backend เสร็จ
ตัวอย่าง workflow:
- เพิ่ม endpoint ใหม่ใน
api/openapi.yaml - เปิด mock server จาก spec
- frontend เรียก mock endpoint
- backend implement endpoint จริง
- contract test ตรวจว่า implementation ตรงกับ spec
Tests
เพิ่ม contract tests เพื่อยืนยันว่า API จริงตรงกับ spec เช่น:
- response มี field ตามที่ประกาศ
- status code ตรงกับที่ระบุ
- request body validate ตาม schema
- ไม่มี breaking change ที่ไม่ได้ตั้งใจ
ถ้า API จริงส่ง field ที่ spec ไม่เคยประกาศ หรือขาด field ที่ spec กำหนดให้ required ควรทำให้ CI fail
เอกสาร
เอกสารอ้างอิง API ควรถูก render จาก OpenAPI โดยตรง ไม่ใช่เขียนตาราง endpoint ด้วยมือ วิธีนี้ทำให้ docs ตรงกับ spec เสมอ เพราะ docs คือ spec ที่ถูกแสดงผลออกมา
SDKs
ถ้าทีมต้องส่ง client library ให้พาร์ทเนอร์หรือทีมภายใน สามารถ generate SDK จาก OpenAPI เดียวกันได้ ทำให้ type, endpoint และ schema สอดคล้องกับสัญญาปัจจุบัน
หลักการคือ: เปลี่ยน input หนึ่งไฟล์ แล้วสร้าง output ทั้งหมดใหม่ให้สอดคล้องกัน
Apidog ทำให้ข้อกำหนดเป็นแหล่งความจริงเพียงแหล่งเดียวได้อย่างไร
การทำ spec-as-code เองทั้งหมดมักต้องประกอบหลายส่วนเข้าด้วยกัน เช่น CLI สำหรับ lint, mock server, documentation generator, test runner และ Git hooks
Apidog รวม workflow เหล่านี้ไว้ในที่เดียว โดยยังให้ OpenAPI spec เป็นศูนย์กลาง
โหมด Spec-First ของ Apidog ถือว่าไฟล์ OpenAPI เป็นคำจำกัดความหลัก คุณออกแบบ endpoint จาก spec และให้ mocks, tests และ docs อ้างอิงจาก spec เดียวกัน
จุดสำคัญคือการซิงค์ Git แบบสองทาง Apidog สามารถอ่านไฟล์ OpenAPI จาก repo และเขียนการเปลี่ยนแปลงกลับไปได้ ทำให้ไฟล์ใน Git และโปรเจกต์ใน Apidog สอดคล้องกัน
workflow ที่ใช้ได้จริง:
- เก็บ
api/openapi.yamlใน repo - เชื่อม repo กับ Apidog
- ออกแบบหรือแก้ endpoint ใน Apidog เมื่อต้องการ UI ที่เร็วกว่า
- sync การเปลี่ยนแปลงกลับเข้า Git
- เปิด PR เพื่อ review diff ของ spec
- ให้ CI ตรวจ spec และ contract tests
วิธีนี้ทำให้ทีมใช้ visual editor ได้ โดยไม่เสียหลักการว่า Git คือที่เก็บสัญญาหลัก สำหรับรายละเอียดการ push การเปลี่ยนแปลงกลับ upstream ดูที่ วิธีการซิงค์ OpenAPI spec ของคุณไปยัง GitHub
ผลลัพธ์คือ OpenAPI ยังคงเป็นแหล่งความจริงเพียงแหล่งเดียว และเครื่องมือ visual ทำงานอยู่บนไฟล์นั้น ไม่ได้แทนที่ไฟล์นั้น
ข้อผิดพลาดที่พบบ่อย
Spec-as-code เริ่มต้นไม่ยาก แต่มีจุดที่ทีมมักพลาด
1. มี spec แต่ไม่ตรวจ implementation
การเขียน OpenAPI อย่างเดียวไม่พอ ถ้าไม่มี test ตรวจว่า API จริงตรงกับ spec ทั้งสองจะค่อย ๆ drift ออกจากกัน
สิ่งที่ควรทำ:
- เพิ่ม contract tests ใน CI
- run test ทุก PR หรือก่อน deploy
- fail build เมื่อ response ไม่ตรงกับ spec
2. ผสม spec ที่ generate กับ spec ที่เขียนมือโดยไม่มีหลักชัดเจน
ทีมควรตัดสินใจว่าแหล่งจริงคืออะไร:
- เขียน OpenAPI เป็น source of truth แล้วให้ code ตรวจตาม spec
- หรือ generate OpenAPI จาก code annotation แล้วใช้ผลลัพธ์นั้นเป็น artifact
ปัญหาเกิดเมื่อผสมทั้งสองแบบโดยไม่กำหนดว่าไฟล์ไหนเชื่อถือได้ ถ้าเลือก spec-first ให้ treat code annotations เป็นตัวช่วย validate ไม่ใช่สำเนาหลักอีกชุดหนึ่ง
3. ถือว่า spec เป็นแค่เอกสาร
OpenAPI ที่มีไว้ให้อ่านอย่างเดียวคือเอกสาร แต่ OpenAPI ที่ใช้สร้าง mocks, tests, docs และ SDKs คือสัญญา
คุณค่าของ spec-as-code เกิดเมื่อไฟล์นี้ถูกผูกกับ workflow จริง ไม่ใช่แค่ commit ไว้เฉย ๆ
4. ข้ามการ review
ถ้า spec อยู่ใน Git แต่ merge ได้โดยไม่มี review ก็ยังเสี่ยงพอ ๆ กับการเปลี่ยนโค้ดโดยไม่มี review
แนวทางขั้นต่ำ:
- ทุกการแก้
api/openapi.yamlต้องผ่าน PR - reviewer ต้องดู breaking changes
- CI ต้อง lint spec
- ถ้ามี breaking change ให้ระบุใน PR description
เริ่มต้นใช้งาน
คุณไม่จำเป็นต้องย้ายทั้งระบบในครั้งเดียว เริ่มทีละขั้นได้
ขั้นที่ 1: Commit OpenAPI เข้า repo
ย้ายไฟล์ spec ไปไว้ใน path ที่แน่นอน เช่น:
api/openapi.yaml
แล้ว commit เข้า Git:
git add api/openapi.yaml
git commit -m "Add OpenAPI specification"
ขั้นที่ 2: บังคับ review ผ่าน PR
ตั้งกฎให้การเปลี่ยนแปลง spec ต้องผ่าน pull request เช่นเดียวกับโค้ด
สิ่งที่ควรดูใน PR:
- มี endpoint ถูกลบหรือไม่
- required field เปลี่ยนหรือไม่
- response schema เปลี่ยน type หรือไม่
- enum ถูกลดค่าหรือเปลี่ยนความหมายหรือไม่
- status code เปลี่ยนหรือไม่
ขั้นที่ 3: สร้าง output อย่างน้อยหนึ่งอย่าง
เริ่มจากสิ่งที่ทีมได้ประโยชน์เร็วที่สุด เช่น docs หรือ mocks
ตัวอย่างเป้าหมายแรก:
- render API docs จาก
api/openapi.yaml - เปิด mock server จาก spec
- generate SDK สำหรับ internal client
ขั้นที่ 4: เพิ่ม CI เพื่อตรวจ OpenAPI
เพิ่มขั้นตอน lint หรือ validate spec ในทุก PR เพื่อจับ YAML หรือ OpenAPI ที่ไม่ถูกต้องก่อน merge
ตัวอย่างแนวคิดของ pipeline:
name: Validate API Spec
on:
pull_request:
paths:
- "api/openapi.yaml"
jobs:
validate-openapi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate OpenAPI spec
run: |
echo "Run your OpenAPI validator here"
เลือก validator ที่ทีมใช้ได้สะดวก ประเด็นสำคัญคือ CI ต้อง fail เมื่อ spec ไม่ถูกต้อง
ขั้นที่ 5: ปิดวงจรด้วย contract tests
เมื่อ docs หรือ mocks ทำงานแล้ว ให้เพิ่ม test ที่ตรวจ API จริงกับ spec ที่ commit ไว้
เป้าหมายคือทำให้ความไม่ตรงกันถูกพบใน pipeline ไม่ใช่โดยลูกค้าหรือพาร์ทเนอร์
สรุป
Spec-as-code คือการทำให้ OpenAPI เป็นสัญญาหลักของ API:
- เก็บเป็นไฟล์ใน Git
- review ผ่าน PR
- validate ใน CI
- ใช้สร้าง mocks, tests, docs และ SDKs
- ตรวจ implementation จริงให้ตรงกับ spec
เริ่มจากการ commit api/openapi.yaml เพียงไฟล์เดียว แล้วค่อยเพิ่ม automation ทีละส่วน หากต้องการ visual editor พร้อม Git sync ในตัว ลองใช้โหมด Spec-First ของ Apidog เพื่อให้ OpenAPI เป็นแหล่งความจริงเพียงแหล่งเดียวของทีม
คำถามที่พบบ่อย
“API spec as code” เหมือนกับ “docs-as-code” หรือไม่?
ทั้งสองใช้แนวคิดเดียวกัน: เก็บ artifact หลักไว้ใน version control และส่งผ่าน pipeline ปกติของทีม Docs-as-code ใช้กับเอกสารประกอบ ส่วน spec-as-code ใช้กับสัญญา API โดยตรง ในทางปฏิบัติ เอกสารที่สร้างจาก OpenAPI ที่ commit ไว้ก็ถือเป็น docs-as-code ด้วย
ไฟล์ข้อกำหนดควรใช้รูปแบบใด?
OpenAPI ในรูปแบบ YAML เป็นตัวเลือกที่นิยม เพราะอ่านง่ายและ diff ใน PR ได้ชัดเจน ในขณะเดียวกันเครื่องมือยัง parse เพื่อนำไปสร้าง mocks, tests และ SDKs ได้ JSON ก็ใช้ได้ แต่ YAML มักตรวจ diff ได้สะดวกกว่า
ฉันจะป้องกันไม่ให้ข้อกำหนดคลาดเคลื่อนจาก API จริงได้อย่างไร?
เพิ่ม contract tests เข้า CI เพื่อยืนยันว่า service ที่รันอยู่ตรงกับ OpenAPI spec ที่ commit ไว้ ถ้า endpoint ส่ง field ที่ไม่ได้ประกาศไว้ ขาด required field หรือคืน status code ที่ไม่อยู่ใน spec ให้ build fail วงจร feedback นี้คือสิ่งที่เปลี่ยน spec จากเอกสารคาดหวังให้เป็นสัญญาที่ enforce ได้จริง



Top comments (0)