--- description: Guardrails for OpenAPI-generated code and spec files (Swift projects) globs: [] alwaysApply: true --- # OpenAPI Generated Code and Spec Guardrails Never hand-edit OpenAPI-generated Swift sources or OpenAPI specs. The spec is the source of truth: generated code is a build artifact derived from it, and any hand edit is lost on the next generation. When a change seems necessary, change the spec, regenerate, and put your own code in hand-written modules, or propose a spec change without touching the generated artifacts. ## Core rules ### Rule 1: Do-not-touch zones Do not edit files that are: - Generated by the OpenAPI generator. - Located under typical generation paths: - `Sources/**/Generated/**` - `.build/**` - `DerivedData/**` - `**/OpenAPI/Generated/**` - The OpenAPI spec(s): `openapi.yaml`, `openapi.yml`, `**/openapi/*.yaml`. ### Rule 2: Analysis-only Analyse, do not change. If asked to "analyse only", do not offer diffs for the spec or the generated files. Provide findings plus actionable recommendations for where to apply changes safely. ### Rule 3: Safe change targets Direct code changes to: - Hand-written modules (the API client, the API server, the shared model module). - Adapters, mappers, and extensions in non-generated targets. - Dependency-injected services and repositories. - A separate notes or proposals file for spec-change suggestions. ### Rule 4: Change requests to specs If a spec change is unavoidable: - Produce a proposal file (for example `Docs/OpenAPI-Proposals/YYYY-MM-DD-what-why.md`). - Include the motivation, example payloads, and migration steps. - Do **not** modify the spec inline as part of that proposal. ## Decision tree ``` Need to modify API behaviour? ├─ Is it in generated code? │ ├─ YES → Do NOT modify │ │ ├─ Create an extension in a hand-written module │ │ ├─ Add an adapter / mapper layer │ │ └─ Use dependency injection │ └─ NO → Safe to modify (the API client, the API server, and so on) ├─ Is it the OpenAPI spec? │ ├─ YES → Do NOT modify inline │ │ ├─ Create a proposal under Docs/OpenAPI-Proposals/ │ │ ├─ Include motivation and examples │ │ └─ Wait for approval, then change the spec and regenerate │ └─ NO → Safe to modify └─ Is it a DTO mapping? ├─ Generated DTO → Create a domain model plus a mapper └─ Domain model → Safe to modify ``` ## Implementation patterns ### Pattern 1: Extension on generated types ```swift // CORRECT: extend in a hand-written module // File: ApiClient/Extensions/User+Domain.swift import ApiShared extension Components.Schemas.UserDTO { func toDomain() -> User { User( id: UUID(uuidString: self.id) ?? UUID(), name: self.name, email: self.email ) } } ``` ### Pattern 2: Adapter layer ```swift // CORRECT: adapter in the API client package // File: ApiClient/Adapters/UserAdapter.swift import ApiShared import SharedModels public struct UserAdapter { public static func toDomain(_ dto: Components.Schemas.UserDTO) -> User { User( id: UUID(uuidString: dto.id) ?? UUID(), name: dto.name, email: dto.email ) } public static func toDTO(_ user: User) -> Components.Schemas.UserDTO { Components.Schemas.UserDTO( id: user.id.uuidString, name: user.name, email: user.email ) } } ``` ### Pattern 3: Spec change proposal ````markdown // File: Docs/OpenAPI-Proposals/2025-01-15-add-user-avatar.md # Proposal: add a user avatar field ## Motivation Users need profile pictures for the social features. ## Proposed change Add an `avatarUrl` field to the UserDTO schema. ## Example payload ```yaml UserDTO: type: object properties: id: type: string name: type: string avatarUrl: # NEW type: string format: uri nullable: true ``` ## Migration steps 1. Update the OpenAPI spec. 2. Regenerate the Swift code. 3. Update the domain model. 4. Add a migration for existing users. ```` ## Anti-patterns ### Do NOT modify generated files ```swift // WRONG: editing Generated/Types.swift // This file is overwritten on the next generation. extension Components.Schemas.UserDTO { var fullName: String { ... } // Lost on regenerate. } ``` ### Do NOT edit the spec directly without a proposal ```yaml # WRONG: a direct, undocumented edit to the spec UserDTO: properties: newField: # Added without a proposal type: string ``` ## Checklist Before modifying API-related code: - [ ] Verified the target file is NOT in a `Generated/` folder. - [ ] Verified the target file is NOT the OpenAPI spec. - [ ] Changes go to hand-written modules (the API client, the API server). - [ ] Extensions or adapters created for generated-type customisation. - [ ] Spec changes documented in a proposal file first. - [ ] Domain models separated from DTOs. ## Verification When working with OpenAPI: 1. Check the file path before any edit. 2. Never modify `.build/`, `DerivedData/`, or `Generated/` paths. 3. Create adapters for DTO-to-domain mapping. 4. Document spec changes in proposals first, then change the spec and regenerate.