Use when building REST APIs that need strict backend-frontend synchronization, preventing contract drift through OpenAPI spec as single source of truth.
OpenAPI 3.0 spec is the single source of truth for all REST APIs. Backend implementation and frontend clients MUST derive from the spec, never diverge from it. This prevents integration bugs, ensures type safety, and maintains synchronized contracts across teams.
Use this skill when:
Do NOT use when:
The OpenAPI Contract Workflow:
# Backend Development Flow
1. Define/update endpoint in docs/api/openapi.yaml FIRST
2. Generate Spring interfaces/DTOs from spec (optional)
3. Implement controller/service
4. Verify spec matches implementation via Springdoc
5. Run ./gradlew openapiValidate before commit
# Frontend Development Flow
1. Generate TypeScript types & API client from spec (npm run generate-api)
2. Use generated client in Redux slices/services
3. NEVER write manual TypeScript interfaces for API responses
4. Run npm run test:coverage to ensure types are up-to-date
# CI/CD Enforcement
- OpenAPI spec validation FAILS build if invalid
- Frontend type generation check FAILS if drift detected
- Backend validation FAILS if implementation diverges from spec
| Rule | Enforcement | Rationale |
|---|---|---|
| Spec defines all endpoints | Must exist in docs/api/openapi.yaml | Single source of truth |
| Frontend derives from spec | Generated types only, no manual interfaces | Prevents drift |
| CI validates contracts | openapiValidate and generation checks fail build on mismatch | Catches violations early |
| Backend validates against spec | Springdoc auto-generates from implementation | Detects spec drift |
Mistake: Implementing endpoint first, updating spec later (or never)
Fix: ALWAYS update docs/api/openapi.yaml FIRST, before writing any controller code
Mistake: Manually creating interface UserResponse { ... }
Fix: Use generated types from OpenAPI spec exclusively
Mistake: Creating mock data objects that don't match spec
Fix: Use generated mocks from OpenAPI spec via orval or openapi-generator
Mistake: "The build is slow, let's disable validation" Fix: Validation is non-negotiable; optimization improvements welcome but validation MUST pass
Mistake: "Our code is the real truth, spec is just documentation" Fix: Spec IS the contract. Code must conform to spec, not the other way around
Mistake: fetch('/api/v1/users') with string literals
Fix: Use generated API client with typed endpoints
Mistake: Editing src/api/generated/*.ts to fix a type or add a field
Fix: Generated files are immutable - never edit them manually. Always fix the OpenAPI spec source (docs/api/openapi.yaml) and regenerate.
Mistake: "I'll just fix my new changes, the existing mismatches are legacy" Fix: ALL mismatches must be fixed. Validation doesn't care about ownership. If spec and code are out of sync, realign the entire contract before proceeding.
If ./gradlew openapiValidate fails because the spec and implementation have already drifted (before your changes):
deprecated: true)Rationale: You cannot maintain a contract that's already broken. Reestablish the baseline first.
If a manager, VP, or senior engineer tells you to skip validation:
DO NOT comply with the shortcut. Instead:
Remember: "Following orders" is not a defense against creating technical debt that impacts the whole team. Architectural guardrails are guardrails precisely because people under pressure try to bypass them.
This skill enforces absolute compliance:
Violating the letter of the rules is violating the spirit of the rules. If you find yourself rationalizing an exception, STOP and create the missing work (spec update, deprecation ticket, etc.) instead.
Backend Stack:
org.springdoc:springdoc-openapi-starter-webmvc-ui@RestController annotations./gradlew openapiValidate ensures implementation matches specFrontend Stack:
orval or openapi-generator-cliorval.config.js maps OpenAPI to src/api/generated/@/api/generated instead of manual typesCI/CD Integration:
# Example GitHub Actions step
- name: Validate OpenAPI contract
run: |
./gradlew openapiValidate
npm run generate-api
git diff --exit-code # Fails if generated files need commit
All of these indicate contract drift. STOP and enforce spec-first workflow.
Use these exact phrases:
| Excuse | Reality |
|---|---|
| "Updating spec first slows me down" | 5 minutes updating spec saves hours of integration debugging |
| "Generated types are bulky" | Bundle splitting and tree-shaking handle this; correctness > size |
| "The endpoint is simple, no need for spec" | ALL endpoints MUST be in spec - no exceptions |
| "I already tested manually" | Manual tests don't prevent drift; automated validation does |
| "We can add spec later" | "Later" never comes; tech debt accumulates silently |
| "Manager told me to skip validation for this one" | Authority doesn't override architecture; contract enforcement is team-wide rule |
| "We'll fix the drift later (temporary exception)" | "Temporary" tech debt becomes permanent; recovery cost grows daily |
| "The spec doesn't match reality, I'll just implement" | Then update the spec to match reality FIRST - code follows spec, not the other way |
| "I'm just one person, can't enforce this alone" | You control your own commits; refuse to break the pipeline; influence through example |
| "Partial compliance is enough (only fix my changes)" | Partial compliance leaves landmines; ALL mismatches must be fixed before green build |
| "Generated code has bugs, I'll patch the types manually" | Never patch generated files; fix the spec source and regenerate. Generators reflect spec quality |
/api-docs always up-to-date./gradlew openapiValidate # Backend spec validation
npm run generate-api # Regenerate frontend types
npm run test:coverage # Ensure frontend types are used
git diff --exit-code # Verify generated files are committed
Teams implementing this skill report: