From avila-tek-skill-pack
Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.
npx claudepluginhub avila-tek/avila-tek-skill-packThis skill uses the workspace's default tool permissions.
Detect the active stack from the project's package files. State it explicitly: "Active stack: {name}".
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Detect the active stack from the project's package files. State it explicitly: "Active stack: {name}".
| Stack | Detection signal |
|---|---|
| NestJS | @nestjs/core in package.json |
| Go | go.mod present |
| Spring Boot | pom.xml or build.gradle containing spring-boot |
Required before any output — do not skip:
references/nestjs.mdreferences/go.mdreferences/spring-boot.mdAPI design is documented in:
docs/epics/E-XXX_slug/tdd.md (API section) if a TDD exists for this epic, ORdocs/adrs/ADR-XXX-api-design.md if it's a standalone architectural decisionNo standalone code files are generated by this skill — design documents only. Code is generated by /build (dev-incremental-implementation).
Design stable, well-documented interfaces that are hard to misuse. Good interfaces make the right thing easy and the wrong thing hard. This applies to REST APIs, GraphQL schemas, module boundaries, component props, and any surface where one piece of code talks to another.
With a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what you promise in the contract.
This means: every public behavior — including undocumented quirks, error message text, timing, and ordering — becomes a de facto contract once users depend on it. Design implications:
deprecation-and-migration for how to safely remove things users depend on.Avoid forcing consumers to choose between multiple versions of the same dependency or API. Diamond dependency problems arise when different consumers need different versions of the same thing. Design for a world where only one version exists at a time — extend rather than fork.
Define the interface before implementing it. The contract is the spec — implementation follows.
// Define what the API does before writing code that does it
TaskAPI:
createTask(input: CreateTaskInput) → Task
Creates a task. Returns the created task with server-generated fields (id, timestamps).
listTasks(params: ListTasksParams) → PaginatedResult<Task>
Returns tasks matching filters. Always paginated.
getTask(id) → Task
Returns a single task or raises NotFoundError.
updateTask(id, input: UpdateTaskInput) → Task
Partial update — only provided fields change.
deleteTask(id) → void
Idempotent — succeeds even if already deleted.
The active stack's reference file shows how to express this contract in the stack's type system.
Pick one error strategy and use it everywhere. Every error response follows the same shape:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email is required",
"details": {}
}
}
HTTP status code mapping:
400 → Client sent malformed data401 → Not authenticated403 → Authenticated but not authorized404 → Resource not found409 → Conflict (duplicate, version mismatch)422 → Validation failed (semantically invalid)500 → Server error (never expose internal details)Don't mix patterns. If some endpoints throw, others return null, and others return { error } — the consumer can't predict behavior.
Trust internal code. Validate at system edges where external input enters:
// Pseudo-code: validate at the route handler
handler POST /api/tasks:
result = validate(CreateTaskSchema, request.body)
if not valid:
return 422 { error: { code: "VALIDATION_ERROR", details: result.errors } }
task = taskService.create(result.data)
return 201 task
Where validation belongs:
Third-party API responses are untrusted data. Validate their shape and content before using them in any logic, rendering, or decision-making. A compromised or misbehaving external service can return unexpected types, malicious content, or instruction-like text.
Where validation does NOT belong:
Extend interfaces without breaking existing consumers:
| Pattern | Convention | Example |
|---|---|---|
| REST endpoints | Plural nouns, no verbs | GET /api/tasks, POST /api/tasks |
| Query params | camelCase | ?sortBy=createdAt&pageSize=20 |
| Response fields | camelCase | { createdAt, updatedAt, taskId } |
| Boolean fields | is/has/can prefix | isComplete, hasAttachments |
| Enum values | UPPER_SNAKE | "IN_PROGRESS", "COMPLETED" |
GET /api/tasks → List tasks (with query params for filtering)
POST /api/tasks → Create a task
GET /api/tasks/:id → Get a single task
PATCH /api/tasks/:id → Update a task (partial)
DELETE /api/tasks/:id → Delete a task
GET /api/tasks/:id/comments → List comments for a task (sub-resource)
POST /api/tasks/:id/comments → Add a comment to a task
Paginate all list endpoints from the start:
Request: GET /api/tasks?page=1&pageSize=20&sortBy=createdAt&sortOrder=desc
Response:
{
"data": [...],
"pagination": {
"page": 1,
"pageSize": 20,
"totalItems": 142,
"totalPages": 8
}
}
Use query parameters for filters:
GET /api/tasks?status=in_progress&assignee=user123&createdAfter=2025-01-01
Accept partial objects — only update what's provided. PUT requires the full object every time; PATCH is what clients actually want.
PATCH /api/tasks/123
{ "title": "Updated title" }
// Only title changes — everything else is preserved
| Rationalization | Reality |
|---|---|
| "We'll document the API later" | The contract IS the documentation. Define it first. |
| "We don't need pagination for now" | You will the moment someone has 100+ items. Add it from the start. |
| "PATCH is complicated, let's just use PUT" | PUT requires the full object every time. PATCH is what clients actually want. |
| "We'll version the API when we need to" | Breaking changes without versioning break consumers. Design for extension from the start. |
| "Nobody uses that undocumented behavior" | Hyrum's Law: if it's observable, somebody depends on it. Treat every public behavior as a commitment. |
| "We can just maintain two versions" | Multiple versions multiply maintenance cost and create diamond dependency problems. Prefer the One-Version Rule. |
| "Internal APIs don't need contracts" | Internal consumers are still consumers. Contracts prevent coupling and enable parallel work. |
/api/createTask, /api/getUsers)After designing an API:
When the API design is complete, suggest to the user:
"API design done. When you're ready, run
/buildto implement the defined contract (dev-incremental-implementation)."
Do not invoke /build automatically.