Code Guidelines
Follow the patterns established in this repository. Reference code-guidelines.md at project root for the authoritative source.
Core Patterns
Dependency Injection
Prefer constructor/function injection for side effects:
- DB connections, loggers, auth, clock, external clients
- Wire at edges (app startup, router factories)
- Avoid global singleton imports from deep modules
- Tests supply fakes/in-memory implementations without patching
// Good: injectable
function createUserService(db: Database, logger: Logger) {
return { ... }
}
// Bad: global import
import { db } from '../db'
Error Handling
Use neverthrow Result types for expected failures. See docs/neverthrow.md.
// Good: explicit Result
function findUser(id: string): ResultAsync<User, NotFoundError | DbError>
// Bad: throwing for control flow
function findUser(id: string): Promise<User> // throws on not found
Error conventions:
- Model expected failures as custom errors (
NotFoundError,UnauthorizedError) - Normalize unknown catches with
typedError() - Map internal errors to transport errors intentionally
- Never leak internals to clients
Logging
Use structured logging with pino. See docs/logging.md.
logger.info("user created", { userId: user.id, email: user.email })
Logging rules:
- Log at boundaries (request → router → service)
- Never log secrets (tokens, passwords, cookies)
- Use appropriate levels: error, warn, info, debug
Testing
See docs/testing.md and docs/vitest_config.md.
- Use DI to inject test dependencies
- Prefer real DB for integration tests (testcontainers)
- Use fakes when faster/clearer
- Make tests deterministic (no timing, randomness, shared state)
API Design
Use oRPC for type-safe APIs. See docs/orpc.md.
- Define contracts in routers
- Validate inputs with schemas
- Return typed Results
Documentation
See docs/ for detailed guides:
auth.md- Authentication patternsconfig.md- Configuration managementdb.md- Database conventionsorpc.md- API design with oRPCneverthrow.md- Error handling with Result typestesting.md- Testing strategiestech_choices.md- Technology decisions
When Writing Code
- Check existing patterns in similar files
- Use DI for testability
- Handle errors explicitly with Result types
- Add structured logging at boundaries
- Write tests that use DI
- Update docs if changing workflows
LLM Usage
When using AI assistance:
- Treat output as untrusted input
- Run
pnpm typecheckandpnpm test - Verify security implications
- Watch for: validation gaps, error handling, logging secrets, SQL issues