- name
- jaw-dev-backend
- description
- MUST USE for backend, API, server, or database work — API design, architecture, database optimization, security hardening, error handling, middleware, observability, queues, and long-lived connections. Triggers: backend, API, REST, GraphQL, schema, migration, query optimization, middleware, OTel, caching, Result pattern, server, 백엔드, API 작업, 마이그레이션, 쿼리 최적화.
- metadata
- {"short-description":"Framework-agnostic backend guidance for APIs, architecture, data access, and operations.","keywords":"API, REST, endpoint, middleware, database, ORM, cache, queue, error handling, observability","last-verified":"2026-07-02"}
# Dev-Backend — Production-Grade Backend Engineering
Build reliable, secure, and maintainable server-side applications.
This skill has modular references for specialized guidance — read the relevant ones before coding.
It activates by change surface whenever work primarily touches APIs, servers, services, jobs, data access, schemas, migrations, or operational backend behavior.
> **C0/C1 work (small local patches):** See `dev` §0.0 Work Classifier + §0.1 Patch Fast-Path before reading references.
> **`dev` is canonical:** `dev` §0.2 Rule Classes, §3 Verification Gate, and §5 Safety Rules apply to all work governed by this skill.
## Modular References
| File | When to Read | What It Covers |
| -------------------------------------- | ------------------------------ | ---------------------------------------------------------------------- |
| `references/core/crud-api.md` | C2 ordinary CRUD/resource endpoints | Route/schema/service/query basics, five operations, error+permission mapping |
| `references/core/api-design.md` | New/changed API style, or C3+ API work (C2 ordinary slice: `crud-api.md` alone suffices) | REST conventions, response envelopes, HTTP status, pagination, GraphQL, gRPC, tRPC |
| `references/core/api-lifecycle.md` | API versioning, deprecation, migration | Versioning strategy, RFC 9745/8594 lifecycle, oasdiff CI gate |
| `references/core/architecture.md` | New features at C3+ (C2 ordinary slice: `crud-api.md` alone suffices) | Layered architecture, DDD, SOLID, when to split, monolith vs micro |
| `references/core/anti-slop-backend.md` | New endpoints, classes, or modules | Banned patterns: god classes, raw SQL in services, magic numbers, etc. |
| `references/core/observability.md` | Production deployments | OpenTelemetry, structured logging, distributed tracing, alerting |
| `references/core/health-checks.md` | Production/long-lived services | Liveness, readiness, startup probes, dependency checks |
| `references/core/process-isolation.md` | CPU-bound or untrusted work | worker_threads vs child_process vs separate service, communication, resource limits |
| `references/core/caching.md` | Performance optimization | Redis-compatible (Valkey/Redis) patterns, CDN, connection pooling, cache invalidation |
| `references/stacks/node.md` | Node.js/TypeScript projects | Express/Fastify, middleware, Zod validation, ESM, error handling |
| `references/stacks/python.md` | Python projects | FastAPI/Django, Pydantic, async patterns, testing |
| `references/stacks/database.md` | Database design/optimization | PostgreSQL, MongoDB, indexing, N+1, migrations, ORM comparison |
| `references/core/ml-serving.md` | ML model deployment, GPU inference | vLLM/SGLang runtime selection, FastAPI+GPU patterns, dynamic batching, quantization |
| `references/core/llm-integration.md` | RAG, LLM API integration, prompt engineering | Chunking, hybrid search, vector DB, structured output, LangChain/LlamaIndex 2026 |
| `references/core/mobile-api.md` | Mobile app API patterns | BFF, push notifications, offline sync, mobile auth, API optimization |
Read `api-design.md` + `anti-slop-backend.md` first, then the relevant stack file.
For C2 ordinary slices, `crud-api.md` alone suffices; read `api-design.md`/`architecture.md` for new API styles or C3+ work.
When backend decisions depend on current external API docs, API lifecycle
changes, LLM/RAG provider behavior, dependency freshness, or package/source
evidence, read the active `search` skill and follow its query-rewrite,
source-fetch, and evidence-status rules.
---
## 0. Stack Detection & Architecture Clarification
### Auto-detect (existing projects)
| File Found | Project Type |
| ----------------------------------- | ----------------- |
| `tsconfig.json` | TypeScript (Node) |
| `package.json` (no ts) | JavaScript (Node) |
| `pyproject.toml`/`requirements.txt` | Python |
| `go.mod` | Go |
| `Cargo.toml` | Rust |
If config files exist → detect silently and proceed.
### Architecture Clarification (new or ambiguous projects)
When the request has **unspecified technology or unclear scope**, clarify before coding:
1. **Identify what's ambiguous** from this list:
| Dimension | Options to present |
| ------------ | ----------------------------------------------------------------------------------- |
| API style | REST (default) · GraphQL (BFF/mobile) · gRPC (internal microservices) · tRPC (TS monorepo) |
| Database | PostgreSQL (default, ACID) · MongoDB (flexible schema) · SQLite (embedded) |
| Auth method | JWT + refresh (stateless) · Session-based (simple) · OAuth 2.1 (3rd party) |
| Realtime | Not needed (default) · WebSocket · SSE · Polling |
| Architecture | Monolith (default) · Modular monolith · Microservices |
2. **Recommend one with reasoning**: cite project context. e.g., "Small team → monolith + PostgreSQL + JWT is the simplest starting point."
3. **Over-engineering guard**: A CRUD API *probably* doesn't need GraphQL + microservices + event sourcing. Simple → complex, not the reverse.
4. **One round limit**: 2-3 options → recommend → confirm → proceed.
If the user already specifies clear tech (e.g. "FastAPI로 REST API 만들어줘"), **skip this entirely**.
**Node/framework defaults (verified 2026-07-02):** production uses Active/Maintenance
LTS — Node 24 (Active LTS) for new services, Node 22 (Maintenance). Framework: Fastify
for greenfield Node APIs (schema/Pino/plugin structure); Express 5 for legacy/ecosystem
compatibility; Hono for edge/serverless/multi-runtime Web-Standards APIs. New TS
validation baseline is Zod v4 (read the migration guide before upgrading v3 projects).
Sources: `references/stacks/node.md` § Sources.
For new Node backend source files, prefer `.ts` when the repo supports TypeScript or is greenfield. Inherit `dev` TypeScript strict-compatibility rules.
If backend boundaries are unclear, read existing source-of-truth docs/logs first, then document routes, services, repositories, data stores, and runtime commands in the repo's existing SOT before broad implementation.
---
## 1. Architecture Decision
Before coding, identify the right pattern:
| Team Size | Default Starting Point |
| --------- | ----------------------- |
| 1-3 devs | Modular monolith |
| 4-10 devs | Modular monolith or SOA |
| 10+ devs | Consider microservices |
**Default to monolith.** Extract only when you have a proven need (different scaling, independent deployment, technology mismatch).
See `references/core/architecture.md` for full decision matrices.
### API Protocol Decision
| Protocol | Choose When | Avoid When |
|----------|-------------|------------|
| **REST** | Public/partner APIs, simple CRUD, caching matters | Clients need flexible data shapes |
| **GraphQL** | Mobile/BFF client aggregation, multiple resources per request | Simple CRUD, server-to-server, file uploads |
| **gRPC** | Internal microservices, high-perf binary, bidirectional streaming | Browser clients (without gRPC-Web), public APIs |
| **tRPC** (v11) | TypeScript shared end-to-end: monorepo, internal tools | Polyglot environments, public APIs |
**Hybrid pattern (verified 2026-07-02):**
```
Public/Partner → REST (OpenAPI 3.1+; prefer 3.2 where tooling supports it)
Mobile/Web BFF → GraphQL (client aggregation; Apollo Federation ONLY when multiple
independently-owned subgraphs must compose into a supergraph)
Internal services → gRPC (Protobuf contracts)
TS internal tools → tRPC v11 (zero-codegen type safety)
```
Deprecations: emit `Deprecation` (RFC 9745) and optional `Sunset` (RFC 8594) headers,
but client adoption is uneven — always pair with OpenAPI/changelog dates and deprecated-
endpoint traffic dashboards.
See `references/core/api-design.md` for protocol-specific patterns.
### Long-Lived Connection Operation
Rules for SSE, WebSocket, and any connection held open beyond a single request-response cycle.
**Lifecycle Rules:**
| Parameter | Default | Rationale |
|-----------|---------|-----------|
| Heartbeat interval | 15-30s | Detect dead connections before TCP timeout (varies by proxy) |
| Reconnection backoff | Exponential 1s-30s with jitter | Prevent thundering herd on server restart |
| Max connection duration | 1h (SSE), 24h (WebSocket) | Force reconnect to rebalance and prevent memory leaks |
| Connections per client | Cap at 6 (SSE) or 1-2 (WebSocket) | Browser limits + server memory budget |
**Server-Side Requirements:**
- **Connection registry:** Track all active connections in-memory (Map by client/session ID). Required for graceful drain and debugging.
- **Graceful drain on deploy:** Stop accepting new connections → send "reconnect" frame to existing → wait drain timeout → close.
- **Memory budget:** Allocate max memory per connection (e.g., 2KB buffer). Monitor total; reject new connections when approaching limit.
- **Backpressure:** If client stops consuming, buffer up to N messages then drop oldest or disconnect.
**Pattern — "202 + Job ID" for Long Operations:**
Instead of holding a connection open for a slow operation:
```
POST /generate → 202 { jobId: "j_abc123" }
GET /jobs/j_abc123 → { status: "processing", progress: 0.6 }
→ { status: "complete", result: {...} }
```
Use SSE/WebSocket only for push notifications about job status — not for the operation itself.
**Banned:**
| Banned | Fix |
|--------|-----|
| Unbounded connections (no cap, no registry) | Connection registry + cap per client + global max |
| No heartbeat (rely on TCP keepalive only) | Application-level heartbeat every 15-30s |
| Blocking event loop per connection (sync work in message handler) | Offload to worker thread or queue; handler stays async |
| Holding connection open for >5s synchronous work | Return 202 + job ID; notify via push when done |
| No reconnection logic on client side | Implement exponential backoff with jitter |
---
## 2. Layered Architecture (Default; Allow Serverless Handlers, Vertical Slices, and Small Scripts When Appropriate)
```
Routes → Controllers → Services → Repositories → Database
│ │ │ │
│ │ │ └── Data access only
│ │ └── Business logic (validation at controller boundary — service trusts caller per dev-architecture §4)
│ └── Parse HTTP, format response
└── URL mapping, middleware
```
**Rules:**
- Routes: URL patterns + middleware only. No logic.
- Controllers: parse input, call services, format output. No business rules.
- Services: receive/return plain data (not `req`/`res`). All logic here.
- Repositories: abstract DB access. Services access data through repositories only.
### Repository Pattern (Interface Abstraction)
**Boundary parsing contract (BACKEND-BOUNDARY-01, DEFAULT).** Parse once at
ingress and trust boundaries; inside that boundary, typed values are proof. Do not
duplicate schema validation, null defense, or defensive parsing in services unless the
data crosses a **new** trust boundary. Canonical boundary-defense ownership stays with
`jaw-dev-architecture` §4; this is the backend stub.
The reason to state it as a rule rather than a preference: duplicated validation reads as
prudence and behaves as ambiguity — with two checks, neither one is the boundary, and a
later change can remove whichever looks redundant.
**Server runtime safety (BACKEND-RUNTIME-01, DEFAULT).** Production server runtimes set
explicit read, write, request/idle, and graceful-shutdown timeouts. Drain on SIGTERM or
deploy: stop accepting new work, let in-flight work finish within the shutdown budget,
then close. Propagate the request id from ingress into structured logs, traces, queued
work, and outbound calls wherever the stack supports it — an id that stops at the queue
boundary is the one you will miss during an incident.
Use repository interfaces so services depend on abstractions, enabling mocking and swapping implementations.
### Async Task Queue Patterns
When work exceeds what an HTTP response cycle should hold open, use a queue.
**Decision: Queue vs Direct:**
| Condition | Use Queue | Use Direct |
|-----------|-----------|------------|
| Execution time >5s | Yes | No |
| Must be retryable on failure | Yes | No |
| Fire-and-forget (caller doesn't wait) | Yes | No |
| <1s, idempotent, caller needs immediate result | No | Yes |
| Real-time user-facing validation | No | Yes |
**Pattern — Accept, Queue, Notify:**
```
1. Client → POST /tasks → Server validates, enqueues
2. Server → 202 { jobId } → Client receives immediately
3. Worker → picks from queue → executes task
4. Client → GET /tasks/{id} → polls status (or receives webhook/SSE push)
5. Worker → completes → writes result, triggers notification
```
**Queue Selection Guide:**
| Queue | When | Notes |
|-------|------|-------|
| **BullMQ** (Redis-compatible) | Node.js, need retries + priorities + rate limiting | Most mature Node queue; needs Redis/Valkey |
| **Celery** (Redis/RabbitMQ) | Python, distributed workers, periodic tasks | De facto Python standard |
| **pg-boss** (PostgreSQL) | Node.js, Postgres is the durable system of record, moderate scale | No extra infra; SKIP_LOCKED-based |
| **Simple DB queue** (polling) | Small scale (<100 jobs/min), any language | `status` column + `SELECT FOR UPDATE SKIP LOCKED` |
| **SQS / Cloud Tasks** | Serverless, managed, very high scale | No infra to manage; at-least-once delivery |
| **Temporal** | Durable multi-step workflows: sagas, human-in-loop, long-running AI/business processes | Workflow engine, NOT a default queue replacement |
**Required Safeguards:**
| Safeguard | Rule |
|-----------|------|
| Idempotency key | Every enqueue call must include a unique idempotency key; dedup on insert |
| Dead letter queue (DLQ) | Failed 3x (configurable) → move to DLQ → alert → manual review |
| Max retries | Set explicit limit (default: 3); exponential backoff between attempts |
| Timeout per job | Every job has a max execution time; kill and retry on exceed |
| Visibility timeout | Lock duration > expected execution time; prevent duplicate processing |
Auf GitHub ansehen