Skip to content

backend

FieldValue
TypeSkill
Source~/.copilot/skills/backend/SKILL.md
DescriptionGo 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

GroupNameSource
ReferencesAPI Contract Design~/.copilot/skills/backend/references/api-design.md
ReferencesConcurrency~/.copilot/skills/backend/references/concurrency.md
ReferencesDatabase~/.copilot/skills/backend/references/database.md
ReferencesDefensive coding~/.copilot/skills/backend/references/safety.md
ReferencesDependencies~/.copilot/skills/backend/references/dependencies.md
ReferencesError handling~/.copilot/skills/backend/references/errors.md
ReferencesGo Service Scaffolding~/.copilot/skills/backend/references/go-scaffolding.md
ReferencesGo Service Standards~/.copilot/skills/backend/references/go-standards.md
ReferencesLayering: handler -> service -> repository -> domain~/.copilot/skills/backend/references/layering.md
ReferencesLinting~/.copilot/skills/backend/references/lint.md
ReferencesObservability~/.copilot/skills/backend/references/observability.md
ReferencesOpenAPI as the source of truth~/.copilot/skills/backend/references/openapi.md
ReferencesPerformance~/.copilot/skills/backend/references/performance.md
ReferencesSecurity~/.copilot/skills/backend/references/security.md
Referencessqlc vs GORM~/.copilot/skills/backend/references/sqlc.md
ReferencesTerminal User Interfaces (TUI)~/.copilot/skills/backend/references/tui.md
ReferencesTestcontainers~/.copilot/skills/backend/references/testcontainers.md
ReferencesTesting~/.copilot/skills/backend/references/testing.md
ResourcesLints~/.copilot/skills/backend/lints.toml
ScriptsLint~/.copilot/skills/backend/scripts/lint.py
ScriptsLint_go~/.copilot/skills/backend/scripts/lint_go.sh
ScriptsLint_openapi~/.copilot/skills/backend/scripts/lint_openapi.sh
ScriptsVerify_scaffold~/.copilot/skills/backend/scripts/verify_scaffold.sh
TemplatesOpenapi Error Envelope~/.copilot/skills/backend/templates/openapi-error-envelope.yaml

Source Content

Backend Services

DomainGo services: code standards, scaffolding, and API contract design
RoleStandard reference for Go code and service architecture; API contract authority before handler code ships
ScopeLayering, error handling, concurrency, testing, observability, databases, security, performance, TUI, new-service scaffolding, REST/OpenAPI/tRPC/GraphQL/gRPC contracts
OutputGo 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…ReadGate with
Writing or reviewing Go service codereferences/go-standards.mdscripts/lint.py --git-changed (runs lint_go.sh)
Scaffolding a new Go Fiber servicereferences/go-scaffolding.mdscripts/verify_scaffold.sh
Designing a new API (REST/tRPC/GraphQL/gRPC) or versioning strategyreferences/api-design.mdscripts/lint.py --git-changed (runs lint_openapi.sh)
Deciding sqlc vs GORM, or migrating between themreferences/sqlc.md-
Writing a Postgres integration test with testcontainersreferences/testcontainers.md-
Deciding which layer code belongs in (handler/service/repo/domain)references/go-standards.md § Layering-
Writing error handling and wrappingreferences/go-standards.md § Error Handling-
Structuring concurrency safelyreferences/go-standards.md § Concurrency-
Writing table-driven tests and hitting coverage floorreferences/go-standards.md § Testing-
Wiring Zap, OpenTelemetry, Prometheus, pprofreferences/go-standards.md § Observability-
Working with databases (pgx/GORM transactions, pagination, scanning)references/go-standards.md § Database-
Security coding checklistreferences/go-standards.md § Security-
Catching defensive-coding gotchasreferences/go-standards.md § Safety-
Performance profiling and benchmarkingreferences/go-standards.md § Performance-
Configuring lint checksreferences/go-standards.md § Lint-
Managing dependenciesreferences/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/conc instead 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 → database skill (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-architect skill.
  • Full security audit of a shipped feature → security skill (this skill’s security.md is the coding-time checklist, not an audit).
  • Kubernetes manifest deep-dive → platform skill.
  • Validation-layer schema modeling → zod-schema-architect skill (TanStack Router/Query on the frontend).
  • UI-side data fetching patterns → frontend skill.

The layering: handler → service → repository → domain

Dependencies point inward only. The domain package is the center and imports nothing from the outer layers.

LayerOwnsKnows aboutNever touches
handlerHTTP: parse, validate, call a service, map errors to statusFiber, generated OpenAPI typesSQL, gorm.DB, business rules
serviceBusiness logic, orchestration, transactionsdomain + repository interfacesFiber, *fiber.Ctx, HTTP status
repositoryPersistence: SQL, pgx/GORM, row mappingdomain typesHTTP, business rules
domainEntities, value objects, domain errors, pure logicnothing inwardevery 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 %w and 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 with errors.Is. Typed errors when callers need fields, extracted with errors.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 panic for 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 go keyword in service code. Use sourcegraph/concconc.WaitGroup, pool.ResultErrorPool, stream.
  • Every blocking call takes a context.Context as its first parameter and honors cancellation.
  • -race is non-negotiabletask test runs go test -race ./... every time. A data race is a failed build.

Testing

  • Table-driven by default. One []struct of cases, one t.Run(tc.name, ...) loop.
  • Coverage floor: ≥ 80% on domain and service. 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-cover enforces 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_id and span_id injected from the active OTel span on every entry. Wide events: one log line per operation, static grep-stable msg, dynamic values in fields (ADR-023).
  • OpenTelemetry tracing via otelfiber middleware; context propagates through service and repository.
  • Prometheus /metrics endpoint; 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.yaml is authoritative. Code is generated from it, never the reverse.
  • oapi-codegen generates 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 after Begin(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 UPDATE when needed.
  • Retry the whole transaction on Postgres error 40001 (serialization failure).
  • Cursor (keyset) pagination over OFFSETOFFSET scans and discards every skipped row; a cursor with an index does not.
  • pgx.CollectRows + RowToStructByName for scanning; pointer fields over sql.NullString/sql.NullInt64.
  • pgx.ErrNoRows maps to domain.ErrNotFound at 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 alg header. Argon2id with OWASP parameters for password hashing.
  • __Host-/__Secure- cookie prefixes on session cookies, with HttpOnly, Secure, and SameSite set.
  • 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.
  • GOMEMLIMIT at 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-Key on 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 }); never OFFSET.
  • Generate the specoapi-codegen (Go) or zod-to-openapi (TS).
  • Lint & mock — Spectral with org ruleset fails CI; prism mock spec.yaml so frontend builds in parallel.
  • Validate. scripts/lint_openapi.sh openapi.yaml runs Spectral + house-rule checks; fix to zero errors.

How I work (Go code)

  1. Place the code. Name the layer first (handler / service / repository / domain); if it is doing two layers’ jobs, split it.
  2. Define the seam as an interface in the consuming layer; inject the implementation in main.
  3. Write the failing test first (RED), table-driven, then make it pass (GREEN), then refactor.
  4. Wrap every error crossing a boundary with %w + context; map to HTTP only in the handler.
  5. Reach for conc for any concurrency; thread context.Context through.
  6. Wire observability in the same change — Zap with trace correlation, an OTel span, a RED metric.
  7. Regenerate from OpenAPI, commit the output, and confirm no drift.
  8. Run task lint test with -race; confirm the coverage floor on domain/service.
  9. Run scripts/lint_go.sh from the module root for a single pass/fail signal.

How I work (scaffolding)

  1. Clarify — service name, primary resource, DB or no, public or internal, expected RPS.
  2. Layout — clean architecture under internal/ (handler / service / repository / domain / config / observability).
  3. Middleware order — recover → otel → zap → metrics → auth → routes. Logger has trace_id + span_id injected.
  4. DB — pgxpool + sqlc (preferred for complex SQL) or GORM. Atlas declarative migrations.
  5. OpenAPIapi/openapi.yamloapi-codegen → implement the generated handler interface.
  6. Tests — table-driven with testify; integration via testcontainers-go against real Postgres.
  7. Ship — multi-stage Dockerfile → distroless; Helm chart; CI signs (cosign) and scans (Trivy).
  8. Verify — run scripts/verify_scaffold.sh [repo-dir] to confirm the repo builds, has the layout, and tests pass.

How I work (API contracts)

  1. Clarify — who calls this, from where, how often, what auth model.
  2. Pick protocol — TS-only internal → tRPC; polyglot/public → OpenAPI 3.1; graph-shaped → GraphQL; perf-critical → gRPC.
  3. Define resources & operations — plural nouns, correct HTTP verbs, Idempotency-Key on POST.
  4. Define error envelope — RFC 9457 Problem Details, never bare strings.
  5. Define pagination — cursor-based, never OFFSET for >10k rows.
  6. Generate the specoapi-codegen (Go) or Zod-to-OpenAPI (TS).
  7. Lint & mock — Spectral fails CI; prism mock so frontend builds in parallel; commit Bruno/Hurl tests.
  8. Validate. scripts/lint_openapi.sh runs 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-lint all 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/service coverage 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-Key on 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 naked go func() in internal/.

Not yet script-checked — design-review claims until a future parser upgrade (Wave 3, docs/improvement-plan-2026-07-09.md):

  • Dependencies point inward: domain imports 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, /metrics is live, pprof is gated — wired in the same change as the feature.

  • OpenAPI generated code is committed and drift-free (task openapi-check in 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.yaml file.

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, %w wrapping, naked go 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-layer internal/ layout, .p3/helm/, Dockerfile, CI, Taskfile, tests pass, no naked go 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 adr skill; 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