backend
| Field | Value |
|---|---|
| Type | Skill |
| Source | ~/.copilot/skills/backend/SKILL.md |
| Description | Go backend services: code standards (layering, error handling, concurrency, testing, observability, databases), new Fiber service scaffolding with clean architecture and Helm, and API contract design (REST/OpenAPI/tRPC/GraphQL/gRPC) with RFC 9457 errors and cursor pagination. Triggers: “review this Go handler”, “new Go service”, “design an API”, “scaffold Go”, “fiber api”, “go test”, “wrap this error”, “table-driven test”, “layering”, “concurrency”, “OpenAPI spec”, “tRPC router”, “GraphQL schema”, “Prism mock”, “Go TUI”, “bubbletea”, “terminal UI”, “Postgres transaction”, “profile this”, “golangci-lint”. |
Bundled Pages
| Group | Name | Source |
|---|---|---|
| References | API Contract Design | ~/.copilot/skills/backend/references/api-design.md |
| References | Concurrency | ~/.copilot/skills/backend/references/concurrency.md |
| References | Database | ~/.copilot/skills/backend/references/database.md |
| References | Defensive coding | ~/.copilot/skills/backend/references/safety.md |
| References | Dependencies | ~/.copilot/skills/backend/references/dependencies.md |
| References | Error handling | ~/.copilot/skills/backend/references/errors.md |
| References | Go Service Scaffolding | ~/.copilot/skills/backend/references/go-scaffolding.md |
| References | Go Service Standards | ~/.copilot/skills/backend/references/go-standards.md |
| References | Layering: handler -> service -> repository -> domain | ~/.copilot/skills/backend/references/layering.md |
| References | Linting | ~/.copilot/skills/backend/references/lint.md |
| References | Observability | ~/.copilot/skills/backend/references/observability.md |
| References | OpenAPI as the source of truth | ~/.copilot/skills/backend/references/openapi.md |
| References | Performance | ~/.copilot/skills/backend/references/performance.md |
| References | Security | ~/.copilot/skills/backend/references/security.md |
| References | sqlc vs GORM | ~/.copilot/skills/backend/references/sqlc.md |
| References | Terminal User Interfaces (TUI) | ~/.copilot/skills/backend/references/tui.md |
| References | Testcontainers | ~/.copilot/skills/backend/references/testcontainers.md |
| References | Testing | ~/.copilot/skills/backend/references/testing.md |
| Resources | Lints | ~/.copilot/skills/backend/lints.toml |
| Scripts | Lint | ~/.copilot/skills/backend/scripts/lint.py |
| Scripts | Lint_go | ~/.copilot/skills/backend/scripts/lint_go.sh |
| Scripts | Lint_openapi | ~/.copilot/skills/backend/scripts/lint_openapi.sh |
| Scripts | Verify_scaffold | ~/.copilot/skills/backend/scripts/verify_scaffold.sh |
| Templates | Openapi Error Envelope | ~/.copilot/skills/backend/templates/openapi-error-envelope.yaml |
Source Content
Backend Services
| Domain | Go services: code standards, scaffolding, and API contract design |
| Role | Standard reference for Go code and service architecture; API contract authority before handler code ships |
| Scope | Layering, error handling, concurrency, testing, observability, databases, security, performance, TUI, new-service scaffolding, REST/OpenAPI/tRPC/GraphQL/gRPC contracts |
| Output | Go code conforming to ADR-024/ADR-027/ADR-023; production-ready scaffolded repos; API specs and mocks |
Absorbs: go-standards, golang-fiber-bootstrapper, api-designer. The skills this absorbed govern: how Go code is written (layering, testing, observability), how to start a new service repo from scratch, and how API contracts are locked in before any handler ships.
Route by task
| You’re… | Read | Gate with |
|---|---|---|
| Writing or reviewing Go service code | references/go-standards.md | scripts/lint.py --git-changed (runs lint_go.sh) |
| Scaffolding a new Go Fiber service | references/go-scaffolding.md | scripts/verify_scaffold.sh |
| Designing a new API (REST/tRPC/GraphQL/gRPC) or versioning strategy | references/api-design.md | scripts/lint.py --git-changed (runs lint_openapi.sh) |
| Deciding sqlc vs GORM, or migrating between them | references/sqlc.md | - |
| Writing a Postgres integration test with testcontainers | references/testcontainers.md | - |
| Deciding which layer code belongs in (handler/service/repo/domain) | references/go-standards.md § Layering | - |
| Writing error handling and wrapping | references/go-standards.md § Error Handling | - |
| Structuring concurrency safely | references/go-standards.md § Concurrency | - |
| Writing table-driven tests and hitting coverage floor | references/go-standards.md § Testing | - |
| Wiring Zap, OpenTelemetry, Prometheus, pprof | references/go-standards.md § Observability | - |
| Working with databases (pgx/GORM transactions, pagination, scanning) | references/go-standards.md § Database | - |
| Security coding checklist | references/go-standards.md § Security | - |
| Catching defensive-coding gotchas | references/go-standards.md § Safety | - |
| Performance profiling and benchmarking | references/go-standards.md § Performance | - |
| Configuring lint checks | references/go-standards.md § Lint | - |
| Managing dependencies | references/go-standards.md § Dependencies | - |
| Building a terminal UI (Bubbletea/Bubbles/Lipgloss) | references/go-standards.md § Terminal UIs | - |
| Validating anything already written | - | scripts/lint.py --git-changed (or explicit files) |
Lazy-load references as needed — a small linter check never pays for the whole standards doc. Scripts run every lint registered and skip missing tools with warnings.
Use me for
Go service code standards
- Deciding which layer a piece belongs in (handler / service / repository / domain).
- Writing or reviewing error handling, wrapping, and sentinel/typed errors.
- Structuring concurrency safely with
sourcegraph/concinstead of raw goroutines. - Writing table-driven tests and judging coverage on domain and service code (≥80%).
- Wiring Zap, OpenTelemetry, Prometheus, and pprof correctly from the first commit.
- Settling “is this idiomatic/simple enough” review questions.
- Building a Go terminal UI (Bubbletea/Lipgloss) with the same layering and testing discipline as a service.
- Postgres transactions, pagination, bulk loads via pgx/GORM inside the repository layer.
- Hardening handlers and services against injection, path traversal, crypto/cookie mistakes.
- Catching defensive-coding gotchas — append aliasing, typed-nil interfaces, concurrent map crashes.
- Running a profile-first performance investigation (pprof → benchstat → evidence).
- Deciding whether a new dependency is worth adding.
New service scaffolding
- Greenfield Fiber HTTP service — repo, clean architecture, observability, Helm, CI.
- Splitting a monolith into a new service.
- Adding the standard observability/Helm/CI layer to an existing skeleton.
- Demonstrating org standards on a fresh repo.
API contract design
- Greenfield service: bootstrap the contract before code.
- Adding endpoints to an existing service and keeping shape consistent.
- Cross-team handoff: emit a mock so frontend can build in parallel.
- Versioning decisions: when to v2, when to additively add a field.
- Settling protocol arguments (REST vs tRPC vs GraphQL vs gRPC).
Don’t use me for
- Postgres performance tuning, index design, or schema design →
databaseskill (this skill’s database.md is transaction/pagination/scanning patterns inside the repository layer, not query tuning or schema design). - CI/CD pipeline authoring →
github-actions-architectskill. - Full security audit of a shipped feature →
securityskill (this skill’s security.md is the coding-time checklist, not an audit). - Kubernetes manifest deep-dive →
platformskill. - Validation-layer schema modeling →
zod-schema-architectskill (TanStack Router/Query on the frontend). - UI-side data fetching patterns →
frontendskill.
The layering: handler → service → repository → domain
Dependencies point inward only. The domain package is the center and imports nothing from the outer layers.
| Layer | Owns | Knows about | Never touches |
|---|---|---|---|
handler | HTTP: parse, validate, call a service, map errors to status | Fiber, generated OpenAPI types | SQL, gorm.DB, business rules |
service | Business logic, orchestration, transactions | domain + repository interfaces | Fiber, *fiber.Ctx, HTTP status |
repository | Persistence: SQL, pgx/GORM, row mapping | domain types | HTTP, business rules |
domain | Entities, value objects, domain errors, pure logic | nothing inward | every outer layer |
Repository is an interface defined in the service layer and implemented in repository; the service depends on the interface, not the concrete type (dependency injection — ADR-024).
Simplicity and size limits (ADR-024)
- Function size ≤ 70 lines; file size ≤ 150 lines. Past that, extract — a long function is two functions wearing a trenchcoat.
- Write the simplest code that works. No abstraction, interface, or generic until a second caller exists.
any/interface{}only at protocol boundaries (JSON, OpenAPI, generic containers) — never to dodge a type in business logic.- Inject dependencies; never
new-up a collaborator inside business logic. - Apply standards incrementally when touching existing files — never a big-bang rewrite.
Error handling
- Wrap at every boundary with
%wand context:fmt.Errorf("load invoice %s: %w", id, err). Reader reconstructs the call path from the message chain. - Sentinel errors for expected conditions (
var ErrNotFound = errors.New("not found")), checked witherrors.Is. Typed errors when callers need fields, extracted witherrors.As. - Domain errors live in
domain; the handler is the only layer that maps them to HTTP status (RFC 9457 Problem Details — ADR-027). - Never
panicfor control flow. Panic only on programmer error at startup. - Never discard an error with
_ =unless you write the one-line reason why it is safe.
Concurrency
- No raw
gokeyword in service code. Usesourcegraph/conc—conc.WaitGroup,pool.ResultErrorPool,stream. - Every blocking call takes a
context.Contextas its first parameter and honors cancellation. -raceis non-negotiable —task testrunsgo test -race ./...every time. A data race is a failed build.
Testing
- Table-driven by default. One
[]structof cases, onet.Run(tc.name, ...)loop. - Coverage floor: ≥ 80% on
domainandservice. Handlers and repositories covered by integration tests (testcontainers-go). - TDD cycle (ADR-024): RED → GREEN → REFACTOR.
- Mock at the interface, not the struct.
task test-coverenforces the floor and fails under threshold.
Observability (wired from the first commit — ADR-023)
Not a later milestone.
- Zap for structured JSON logs, with
trace_idandspan_idinjected from the active OTel span on every entry. Wide events: one log line per operation, static grep-stablemsg, dynamic values in fields (ADR-023). - OpenTelemetry tracing via
otelfibermiddleware; context propagates through service and repository. - Prometheus
/metricsendpoint; RED metrics (Rate, Errors, Duration) on every handler via middleware. - pprof on a separate internal port, gated by
DEBUG=true— never exposed on the public listener in prod. - Middleware order is load-bearing: recover → otel → zap → metrics → auth → routes.
OpenAPI as the source of truth
- The spec at
api/openapi.yamlis authoritative. Code is generated from it, never the reverse. oapi-codegengenerates server interface + types; you implement the interface. Generated code is committed.- Regeneration is a build step (
task openapi), and CI fails if generated code drifts. - Contract design is this skill’s job; this section governs the generate-and-implement loop.
Database (Postgres via pgx/GORM)
defer tx.Rollback(ctx)immediately afterBegin(ctx)— a no-op after successful commit, the only reliable cleanup.- Pick the isolation level and row lock deliberately — Read Committed by default,
SELECT ... FOR UPDATEwhen needed. - Retry the whole transaction on Postgres error
40001(serialization failure). - Cursor (keyset) pagination over
OFFSET—OFFSETscans and discards every skipped row; a cursor with an index does not. pgx.CollectRows+RowToStructByNamefor scanning; pointer fields oversql.NullString/sql.NullInt64.pgx.ErrNoRowsmaps todomain.ErrNotFoundat the repository boundary.
Security (coding-time checklist)
- Validate at the boundary — the handler is where untrusted input gets checked.
os.Root(Go 1.24+) for any user-supplied file path — primary defense against path traversal.- Pin the JWT signing algorithm explicitly — never trust the
algheader. Argon2id with OWASP parameters for password hashing. __Host-/__Secure-cookie prefixes on session cookies, withHttpOnly,Secure, andSameSiteset.- Never echo internal error text to a client — the same boundary discipline applied to security.
- pprof stays loopback-only or auth-gated, even behind
DEBUG=true.
Performance (profile first, one variable at a time)
- Baseline → pprof → one change →
benchstat→ evidence in the PR. Never guess-and-change. GOMEMLIMITat 80–90% of the container limit on k8s.- CI benchmark gates compare against a baseline, never an absolute threshold.
API contract design (REST/OpenAPI/tRPC/GraphQL/gRPC)
- Clarify — who calls this, from where, how often, what auth model, what error budget.
- Pick protocol — TS-only internal monorepo → tRPC; polyglot/public → OpenAPI 3.1; graph-shaped reads → GraphQL; perf-critical internal → gRPC + buf.
- Define resources & operations — plural nouns, correct HTTP verbs,
Idempotency-Keyon POST. - Define error envelope — RFC 9457 Problem Details (
type,title,status,detail,instance). Never bare strings. - Define pagination — cursor-based (
?cursor=...&limit=...→{ items, next_cursor }); neverOFFSET. - Generate the spec —
oapi-codegen(Go) orzod-to-openapi(TS). - Lint & mock — Spectral with org ruleset fails CI;
prism mock spec.yamlso frontend builds in parallel. - Validate.
scripts/lint_openapi.sh openapi.yamlruns Spectral + house-rule checks; fix to zero errors.
How I work (Go code)
- Place the code. Name the layer first (handler / service / repository / domain); if it is doing two layers’ jobs, split it.
- Define the seam as an interface in the consuming layer; inject the implementation in
main. - Write the failing test first (RED), table-driven, then make it pass (GREEN), then refactor.
- Wrap every error crossing a boundary with
%w+ context; map to HTTP only in the handler. - Reach for
concfor any concurrency; threadcontext.Contextthrough. - Wire observability in the same change — Zap with trace correlation, an OTel span, a RED metric.
- Regenerate from OpenAPI, commit the output, and confirm no drift.
- Run
task lint testwith-race; confirm the coverage floor on domain/service. - Run
scripts/lint_go.shfrom the module root for a single pass/fail signal.
How I work (scaffolding)
- Clarify — service name, primary resource, DB or no, public or internal, expected RPS.
- Layout — clean architecture under
internal/(handler / service / repository / domain / config / observability). - Middleware order — recover → otel → zap → metrics → auth → routes. Logger has trace_id + span_id injected.
- DB — pgxpool + sqlc (preferred for complex SQL) or GORM. Atlas declarative migrations.
- OpenAPI —
api/openapi.yaml→oapi-codegen→ implement the generated handler interface. - Tests — table-driven with
testify; integration viatestcontainers-goagainst real Postgres. - Ship — multi-stage Dockerfile → distroless; Helm chart; CI signs (cosign) and scans (Trivy).
- Verify — run
scripts/verify_scaffold.sh [repo-dir]to confirm the repo builds, has the layout, and tests pass.
How I work (API contracts)
- Clarify — who calls this, from where, how often, what auth model.
- Pick protocol — TS-only internal → tRPC; polyglot/public → OpenAPI 3.1; graph-shaped → GraphQL; perf-critical → gRPC.
- Define resources & operations — plural nouns, correct HTTP verbs,
Idempotency-Keyon POST. - Define error envelope — RFC 9457 Problem Details, never bare strings.
- Define pagination — cursor-based, never
OFFSETfor >10k rows. - Generate the spec —
oapi-codegen(Go) or Zod-to-OpenAPI (TS). - Lint & mock — Spectral fails CI;
prism mockso frontend builds in parallel; commit Bruno/Hurl tests. - Validate.
scripts/lint_openapi.shruns Spectral + RFC 9457 / cursor pagination / Idempotency-Key checks; fix to zero errors.
Self-rubric
Checked by scripts/lint_go.sh (via scripts/lint.py):
-
go vet,-race,golangci-lintall green (degrade to a warning, never silently pass, if the tool isn’t on PATH). - No function over 70 lines (ADR-024 house rule, grep-checked — file-size is a design guideline, not yet script-enforced).
- Every boundary error wraps with
%w, not%v/%s(grep-checked). - No raw
go func()in non-test code (grep-checked). -
domain/servicecoverage floor (default 80%) holds.
Checked by scripts/lint_openapi.sh (via scripts/lint.py):
- RFC 9457 Problem Details shape present; cursor pagination on list endpoints;
Idempotency-Keyon POST; Spectral clean if installed.
Checked by scripts/verify_scaffold.sh (run once after scaffolding, not part of scripts/lint.py — see references/go-scaffolding.md):
- Builds; six-layer
internal/layout present; Helm/Dockerfile/CI/Taskfile present; tests pass; no nakedgo func()ininternal/.
Not yet script-checked — design-review claims until a future parser upgrade (Wave 3, docs/improvement-plan-2026-07-09.md):
-
Dependencies point inward:
domainimports nothing; handler never touches SQL; service never touches Fiber. -
No abstraction without a second caller; file size stays reasonable even though it isn’t linted.
-
Domain errors are mapped to HTTP status only in the handler.
-
Tests are table-driven (shape, not just presence).
-
Zap+OTel are correlated,
/metricsis live, pprof is gated — wired in the same change as the feature. -
OpenAPI generated code is committed and drift-free (
task openapi-checkin CI is the enforcement point, not this skill’s own script yet). -
Transactions roll back on every error path; retried whole on
40001; pagination is cursor-based in code, not just in the spec. -
Security checklist applied; new dependency was deliberate, not reflex.
-
scripts/lint.py --git-changed(or explicit files) exits 0 for every changed.go/openapi.yamlfile.
References load lazily — a single-file lint check never pays for the whole standards doc. scripts/lint.py runs every lint registered in lints.toml that matches the changed file types, skips missing tools with a warning, and fails only on required lints. To add a lint, append a [[lint]] block to lints.toml — no code changes.
References
references/go-standards.md— complete Go service standards: layering, error handling, concurrency, testing, observability, database, security, safety, performance, lint, dependencies, terminal UIs. See that file for detailed workflow steps.references/go-scaffolding.md— new-service scaffolding workflow, architecture, middleware order, database choice, observability wiring, Helm, CI patterns.references/api-design.md— API contract design: REST, OpenAPI 3.1, tRPC, GraphQL, gRPC, RFC 9457 errors, cursor pagination, versioning, Prism mocks, Spectral linting.references/sqlc.md— sqlc vs GORM decision criteria, sqlc.yaml config, query + generated-code usage, migration path either direction.references/testcontainers.md— container lifecycle, parallel test isolation (per-package vs per-test), module/image caching for faster repeated runs.templates/openapi-error-envelope.yaml— canonical RFC 9457 Problem Details schema + response block, matching this skill’s own error-envelope conventions.scripts/lint_go.sh— self-check:go vet,go test -race,golangci-lint(degrades if not on PATH), plus function size,%wwrapping, nakedgo func(), coverage floor. Run:scripts/lint_go.sh [./path/...].scripts/lint_openapi.sh— lint openapi.yaml: Spectral validation (if installed), RFC 9457 Problem Details shape, cursor pagination on list endpoints, Idempotency-Key on POST. Run:scripts/lint_openapi.sh openapi.yaml.scripts/verify_scaffold.sh— verify a scaffolded repo: builds, has the six-layerinternal/layout,.p3/helm/, Dockerfile, CI, Taskfile, tests pass, no nakedgo func(). A one-shot scaffold check, not a file-change lint — run separately:scripts/verify_scaffold.sh [repo-dir].lints.toml+scripts/lint.py— the lint registry and one-command dispatcher.- ADR-024 (engineering standards), ADR-027 (default tech stack), ADR-023 (observability & wide-events logging) — via the
adrskill; this skill enforces them in Go. - Effective Go · Go Code Review Comments · sourcegraph/conc · oapi-codegen · Fiber v2 · Zap · Prism mock server · RFC 9457 — Problem Details for HTTP APIs