# CLAUDE.md — Go Backend Engineer (API Services)

This is the engineering standard for all Go backend **API services** in the organisation — request/response workloads served over REST, gRPC, or GraphQL. This template is the golden path. Do not deviate from it.

This does **not** apply to background workers, event consumers, cron jobs, or data pipelines. Those have different architectural concerns and their own templates.

## Architecture

This codebase follows a clean layered architecture:

```
transport (REST/gRPC/GraphQL) → usecase → storage
```

- **Transport layer is dumb.** It maps requests to DTOs, calls the usecase, maps the response back. No business logic here. Ever.
- **Business logic lives in `internal/usecase/`.** This is transport-agnostic. It uses plain Go structs and `context.Context`. It never imports transport-specific packages.
- **Storage is behind interfaces.** Never use GORM directly in handlers or usecases. Always go through the repository interface in `internal/storage/database/`.
- **Dependencies are explicit.** All handler dependencies are passed as function arguments (REST) or struct fields (gRPC/GraphQL). Looking at a handler must tell you exactly what it depends on. No globals. No init().

## Adding a new entity/domain

Think in usecases first, not endpoints.

1. **Define the entity** in `internal/storage/database/` — model struct with the base `Model` embed (ULID generation), plus the storage interface.
2. **Define the usecase** in `internal/usecase/<entity>/` — interfaces (`Creator`, `Fetcher`, `Manager`), DTOs, and implementations. This is where business rules live.
3. **Wire the transport** in `internal/transport/{rest,grpc,graphql}/` — handlers that call the usecase. Map transport-specific types (JSON, protobuf, GraphQL) to usecase DTOs.
4. **Register in bootstrap** — add the new storage and usecase manager to `internal/bootstrap/bootstrap.go`.
5. **Write migrations** — use `make scaffold name=<entity>` to generate migration files. Never modify the database schema by hand.

The flow is always: model → usecase → transport → bootstrap. Not the other way around.

## Code conventions

### Structure
- Follow the existing package structure exactly. Do not create new top-level packages.
- Use `internal/` for all application code. Only `pkg/` for truly reusable utilities.
- Generated code (mocks, swagger, GraphQL) is never committed. Run `make generate` locally.

### Naming
- Handlers: `<Entity><Action>` (e.g., `CreateTodo`, `GetTodo`)
- Interfaces: role-based names (`Creator`, `Fetcher`, `Manager`) — not `ITodoService`
- DTOs: match the entity name, live in the usecase package
- Errors: descriptive (`ErrNotFound`, `ErrInvalidArgument`)

### Error handling
- Errors are transport-specific. Each transport layer has its own error mapping:
  - REST: HTTP status codes
  - gRPC: `AppError` with gRPC status codes (`NotFound()`, `InvalidArgument()`, `Internal()`)
  - GraphQL: `gqlerr` package for structured errors
- Usecases return plain Go errors. Transport layers map them to the appropriate format.
- Never swallow errors. Always handle or propagate.

### Configuration
- All config via environment variables with struct tags and sensible defaults.
- Never hardcode configuration values — not URLs, not timeouts, not feature flags.
- Config structs live in `internal/config/`.

### IDs
- Use ULIDs, not UUIDs. The base model handles generation via `BeforeCreate` hook.

## Dependencies and libraries

### Use shared company libraries first
Before reaching for an external library, check if the company shared libs (`company shared libs`) already provide the functionality. This is a hard gate — we do not want multiple open-source libraries solving the same problem across services.

### Do not add new libraries without approval
The template defines the approved dependency set. If a library is not in `go.mod`, it needs a discussion before adding. We govern our dependency tree — ungoverned dependencies become security and maintenance liabilities.

### Key libraries in use
- **HTTP router:** Chi
- **Database:** GORM with golang-migrate
- **gRPC:** google.golang.org/grpc with buf/protovalidate
- **GraphQL:** gqlgen with Apollo Federation v2
- **Testing:** testify (assertions), gomock (mocks), testcontainers-go (integration)
- **Observability:** Prometheus client, company logger package
- **Config:** Company config loader with env var support

## Testing

### Tests are mandatory. No exceptions.

**Unit tests:**
- Colocated with source files (`*_test.go`)
- Use `gomock` for mocking interfaces, `testify` for assertions
- Run with `t.Parallel()` where possible
- Use table-driven tests for multiple cases
- Test the behaviour, not the implementation

**Integration tests:**
- Use build tag `//go:build integration`
- Use testcontainers for real Postgres and Redis — never mock the database in integration tests
- Use `testcontainer.MigrateAndInitializeDB()` for setup
- Containers auto-cleanup via `tb.Cleanup()`

**What to test:**
- Every usecase: happy path + error cases
- Every handler: request mapping, response mapping, error responses
- Every repository method: against a real database (integration)

**Run tests:**
- `make test` — unit tests
- `make test-integration` — integration tests (requires Docker)

## Observability

Observability is not optional. It is wired into the template from the start.

- **Structured logging:** Use the company logger. Always log with context (`logger.InfoContext(ctx, ...)`). Include correlation IDs.
- **Metrics:** Prometheus. HTTP request duration, gRPC request duration, GraphQL resolver duration, panic counter — all pre-configured.
- **Health checks:** Every service exposes `/internal/health` and `/internal/metrics`.
- **Panic recovery:** Built into all transport layers. Panics are logged with stack traces and counted.

Do not add custom observability infrastructure. Use what the template provides.

## Performance

Performance is not an afterthought — it is baked in. But also: KISS.

- Use the database efficiently. No N+1 queries. Use DataLoaders for GraphQL batch fetching.
- Use connection pooling (GORM handles this).
- Set appropriate timeouts — the defaults in config are sensible, but adjust per-service if needed.
- Profile before optimising. Do not add caching, goroutine pools, or complex patterns unless you have measured a problem.
- Keep handlers thin. Heavy computation belongs in the usecase layer where it can be tested and profiled independently.

## What you must never do

1. **Never overhaul the template.** The structure is the standard. Work within it. If you think something fundamental needs to change, that's a conversation with the platform team — not a PR.
2. **Never modify `.github/`.** Workflows are synced from the template repository. Changes will be overwritten.
3. **Never copy-paste code without understanding the structure.** Understand *why* the code is structured this way. The patterns exist for a reason.
4. **Never commit generated code.** Mocks, swagger docs, GraphQL generated files — all regenerated via `make generate`.
5. **Never bypass the shared library gate.** If `company shared libs` doesn't have what you need, raise it. Don't silently add a competing library.
6. **Never write a handler without tests.** Both unit and integration.
7. **Never use raw SQL or direct GORM calls outside the storage layer.** Always go through repository interfaces.
8. **Never hardcode secrets, URLs, or environment-specific values.**

## PR process

### Contract first
Before writing code, agree on the API contract — protobuf definition, GraphQL schema, or OpenAPI spec. The contract is the handshake between teams. Code comes after.

### Keep PRs small
- One concern per PR. If your PR touches usecase, storage, transport, and config — it's too big.
- Stack PRs when working on large features: model → usecase → transport → wiring. Each one is a reviewable, mergeable unit.
- Use conventional commit messages. PR titles must follow the format enforced by CI.

### CI is the gatekeeper
- All tests must pass (unit + integration)
- Linting must pass (golangci-lint)
- Secret scanning must pass (GitGuardian)
- Coverage must be maintained (SonarQube)
- PR title must be a conventional commit

If CI fails, fix it. Do not ask for exceptions.

## Quick reference

```bash
make dev                  # Start Postgres + Redis, run REST server
make test                 # Run unit tests
make test-integration     # Run integration tests
make lint                 # Run linter
make generate             # Generate mocks, swagger, GraphQL code
make scaffold name=post   # Scaffold a new entity
make docs                 # Generate Swagger docs
make clean                # Remove generated files
```

## Transport-specific commands

```bash
go run cmd/main.go serve rest      # Start REST server (:8080)
go run cmd/main.go serve grpc      # Start gRPC server (:50051)
go run cmd/main.go serve graphql   # Start GraphQL server (:8080)
go run cmd/main.go db migrate      # Run database migrations
```
