# Code Style Guide
Follow these conventions when writing code.
---
## Core Philosophy
- **Less is more.** Minimal code, minimal abstraction, minimal API surface. Delete dead code. Every export earns its place.- **Iterate toward simplicity.** Build, evaluate, rebuild until you reach the most minimal abstraction. Don't abstract prematurely.- **Write for the next reader.** Code should be readable without comments. If you need a comment, rewrite the code.- **Correctness above all.** Never return `undefined` to avoid an error. Never swallow failures. If a case isn't planned for, throw.- **Ship, then polish.** Get it working first, iterate later. "Working" means clean enough to read tomorrow.
---
## Language & Runtime
- TypeScript everywhere- Next.js App Router for apps, pure Node.js for libraries- pnpm, pnpm workspaces + Turborepo for monorepos- commander.js/tsx for clis- tsup for library bundling- Biome for linting/formatting- vitest/jest for testing
---
## Naming
Naming is critical. Names _are_ the API.
### Files- **kebab-case** for all files: `activity-logs.ts`, `query-properties-tool.ts`- Test files co-located: `searches.test.ts` alongside `searches.ts`
### Code- **PascalCase**: types, Zod schemas, React components, error classes- **camelCase**: functions, variables, module-level constants- **SCREAMING_SNAKE_CASE**: true constants (enums, string literals)- Prefix domain types with the domain name: `[Domain]User`, `[Domain]Search`- Error classes: `[Domain][Entity][Problem]Error`
### Data Access Functions
These conventions are strict:
| Prefix | Returns | On missing ||--------|---------|------------|| `get` | Single item | **Throws** || `find` | Single item or `null` | Returns `null` || `findMany` / `find*s` | Array (possibly empty) | Returns `[]` || `query` | Paginated results | Returns empty page || `create` | Created item | — || `update` | Updated item | Throws if not found || `upsert` | Created or updated item | — || `insert` | Inserted item | — || `softDelete` | void | — |
Names should be short. Compound names with `by`/`and` are fine when needed: `findEntityWithRelationByOwnerAndId`.
---
## Type System
### Zod as Single Source of Truth
Type and schema share the same name. The TypeScript type is derived from the Zod schema:
```tsexport type MyFilter = z.infer<typeof MyFilter>;export const MyFilter = z.object({  types: z.array(MyType).optional(),  priceRange: z.tuple([z.number().nullable(), z.number().nullable()]).optional(),});```
### Drizzle Schema Types
```tsexport type MyEntity = InferSelectModel<typeof myEntities>;```
### Discriminated Unions for Events
```tsexport const MyEvents = z.discriminatedUnion("type", [  z.object({ type: z.literal("entity-created"), entity: MyEntity }),  z.object({ type: z.literal("entity-updated"), entityId: z.number() }),]);```
### Entity IDs
Use indexed type access, not bare primitives:
```tsfunction getEntityById(id: MyEntity["id"]): Promise<MyEntity>```
### Interface vs Type- **Interfaces** for contracts that external code implements (pluggable systems)- **Types** (via Zod `z.infer`) for data shapes and domain models
---
## Architecture
### Factory Functions over Classes
Prefer `create*` factory functions returning interface-typed objects. Private state via closures:
```tsexport function createThing(options: ThingOptions): Thing {  let { store } = options;
  return {    doWork() { ... },    configure(opts) { ... },  };}```
---
## API Design
Design the public API first. Start in `lib/[domain]/` with a clear, minimal exported surface. Only export what consumers need.
Exported functions are the primary unit of testing. If the public API is well-tested, internals can change freely.
### Error Handling
**Throw early, catch at boundaries.**
- Base domain error class extending `Error`- Specific subclasses for each failure, all one-liners:
```tsexport class EntityNotFoundError extends DomainError {}export class InsufficientCreditError extends DomainError {}```
- `get*` functions throw when entities are missing- Catch at API/action boundaries with `instanceof` checks- Server actions wrap in `createAction()` returning `{ error: string }`
---
## Database (Drizzle ORM)
- `pgTable` definitions with epoch-based timestamps- `createdAt` with `.default(sql\`CURRENT_TIMESTAMP\`).notNull()`- Soft deletion via nullable `deletedAt`- JSONB typed with `.$type<T>()`
### Query Patterns
- **CTEs as module-level variables** for reusable filtered views (e.g. undeleted records)- **Dynamic WHERE**: build a `conditions[]` array, apply with `and(...conditions)`- **Pagination**: `page`/`pageSize` params with defaults (`1`, `100`)- **Sort as tuples**: `[key, "ASC" | "DESC"][]`- **Destructure single results**: `const [result] = await db.insert(...).returning()`- **Raw SQL** via `sql` tag for complex queries (PostGIS, JSONB, etc.)
---
## Library Design
### Pluggable Architecture
Define behaviour as interfaces, provide multiple implementations:
```tsexport interface Store {  get(id: string): Promise<unknown | undefined>;  set(id: string, value: unknown): Promise<void>;}
export function createMemoryStore(): Store { ... }    // Dev/testexport function createRedisStore(redis): Store { ... } // Production```
### Optional Methods for Optimizations
```tsexport interface Store {  getAll?(): Promise<Record<string, unknown>>;  // Optional bulk fetch  get(id: string): Promise<unknown | undefined>; // Required fallback}```
### Event Callback Hooks
Optional callbacks invoked with `?.()`:
```tsexport interface Events<T> {  onCreated?(entity: T): Promise<void>;  onUpdated?(entity: T): Promise<void>;}
await events.onCreated?.(entity);```
### Barrel Exports
Explicit re-exports in `index.ts`. Separate `export type` for type-only exports.
---
## Testing
Test infrastructure comes first. Invest in making tests easy to write before writing tests. If tests are hard to write, the API is wrong.
### Philosophy- Test the public API, not implementation details- Pragmatic coverage — test where bugs are likely, skip the obvious- Test cases should be minimal — no setup in the test body- Mock at API boundaries with strong types, never at low levels like `fetch`- Tests inform API design
### Style- `describe` with function reference in app code: `describe(findEntities, () => {})`- `describe` with string in library code: `describe("module", () => {})`- Simple test names: `it("works")`, `it("throws on missing")`- Inline snapshots for complex output- Helper generators: `generateEntity({ overrides })`- Reusable setup in `beforeEach` — never duplicated- Cleanup: `afterEach(() => { jest.clearAllMocks() })`
---
## React & Frontend
- Named function exports (no default exports for components)- Props typed inline, not as separate interfaces- `"use client"` only when needed; server components by default- `useState` for local state, `useSWR` for data fetching- shadcn/ui + Tailwind + Lucide icons- Small focused components; helpers at bottom of file, unexported unless needed- Custom hooks co-located when single-use- For anything requiring browser automation or testing, use the `agent-browser` skill.
---
## Dependencies
Reach for trusted dependencies first. For anything new: research, review code quality/maintenance/security. If it doesn't meet the bar, build it yourself.
**Trusted stack:** TypeScript, Next.js, React, Zod, Drizzle, Vercel AI SDK, Vercel Workflow, Vercel Sandbox, D3, pnpm, tsup, Biome, Jest, shadcn/ui, Tailwind, Lucide, Radix UI, Stripe, SWR, Terraform, Turso, Neon, Supabase
---
## Infrastructure & Deployment
### Platform
- Vercel for deployment. Serverless architecture by default.- Choose infrastructure that fits the serverless model. Not rigid — pick the right tool for the job.
### Database Strategy
- **Non-trivial projects**: start with Postgres on Neon or Supabase.- **Simple/prototype projects**: SQLite locally, Turso in production. Flag when hitting limitations (concurrent writes, complex queries, JSONB) as a signal to migrate to Postgres.- Drizzle ORM + Drizzle Kit always.
### AI & Workflow
- Vercel AI SDK (`ai-sdk.dev`) for AI features- Vercel Workflow (`useworkflow.dev`) for durable workflows- Vercel Sandbox for sandboxed code execution
### Infrastructure as Code
- **Always prefer IaC over manual setup.** Terraform is the tool of choice.- Before provisioning anything on a third-party service, check for a Terraform provider first.- Provision secrets via Terraform where possible.- State: per-project local state.- Terraform files live in `<projectDir>/infra`.- Use the 1Password Terraform provider for secrets at plan/apply time. App runtime secrets via environment variables.
### Secrets
- All tokens, passwords, and credentials are managed via **1Password** and the `op` CLI.- Never hardcode secrets. Never commit them.- Use `op://VaultName/ItemName/fieldName` URI format everywhere — Terraform providers, `.env` files, scripts.- Read a secret: `op read "op://Vault/Item/field"`- Inject into a process: `op run --env-file=.env -- <command>` (replaces `op://` refs at runtime)- In Terraform: use the 1Password provider to reference `op://` URIs directly.- If `op` fails due to authentication, ask for a service token.
### Project Bootstrap
- All projects live in `~/code`. Scaffold new projects there.- `pnpm create next-app --ts --tailwind --biome --app --turbopack --use-pnpm --yes ~/code/<dir>` to scaffold (creates git repo).- Push to GitHub and link to Vercel on explicit instruction.
---
## Project Structure
### Application (Next.js)
```lib/  [domain]/    schema.ts        # Drizzle tables    types.ts         # Zod schemas + types    errors.ts        # Error classes    db.ts            # DB connection    [entity].ts      # Data access    [entity].test.ts # Tests    functions/       # Event handlers (default exports)    tools/           # AI tool definitions    adapters/        # External service adapters
app/  (group)/    page.tsx    layout.tsx    actions.ts       # Server actions    urls.ts          # URL formatters  api/  components/ui/     # shadcn/ui```
### Library (Monorepo)
```packages/  [core]/src/    index.ts         # Barrel exports    types.ts    [module].ts    [module].test.ts    testing.ts       # Test utilities (exported)  [integration]/src/    index.ts         # Single-file implementationexamples/  [example-app]/```
---
## Creative Coding & Visual Design
For creative/visual projects.
### Visual Identity- Dark backgrounds, D3 `interpolatePlasma` as primary color scheme- `mix-blend-mode: screen` for luminous light-on-dark effects- Inverted text (white bg + black text + screen blend) for emphasis instead of bold- Animated gradient titles via `background-clip: text`, slow (10-30s), never distracting- Subtlety — effects should be felt, not noticed- hackerman CLI style, retro futurism
### Principles1. Audio/music vocabulary — `lfo`, `hz`, `sample` — animation as sound2. D3 color scales as identity, not arbitrary hex3. `function*` generators for progressive reveal — show the process4. Time as modular arithmetic for seamless looping5. Parametric everything — sketches are parameter spaces6. Trail/accumulation aesthetics — depth from simplicity
# Work Style
- **Think from first principles.** Before reaching for a pattern, library, or default — ask what the problem actually requires. Decompose new problem spaces into their atomic parts visibly before proposing solutions. Reason from fundamentals, not convention.- Default to the stated technology preferences (Next.js, Vercel, Drizzle, etc.) — they exist for good reason. But if first-principles thinking reveals a better fit, surface it. Prefer the default, challenge it when the fundamentals demand it.- Be precise. No filler, no hedging, no restating the question, no summarizing what was just done. Say what matters, skip what doesn't.- Code output follows the same rule — no placeholder comments, no explanatory prose where the code speaks for itself.- Be autonomous. Make decisions, trust your judgment, keep moving. Only pause when making a large leap or a big assumption — that's when to ask for input.- Give meaningful pushback. Say "I don't think this is a good idea for X reasons" when warranted.- At the end of each plan, interview me in detail using the AskUserQuestionTool about literally anything: technical implementation, UI & UX, concerns, tradeoffs, etc. but make sure the questions are not obvious.
## One-Way Doors
Before committing to a decision that's hard to reverse, explicitly name it as a **one-way door** and confirm before proceeding. This includes:
- Architectural patterns that propagate across the codebase- Third-party service or framework lock-in- Data model choices that touch many consumers- Public API contracts
Prefer the reversible approach when the cost difference is small. Not every decision needs a gate — routine database migrations and incremental changes are fine. The point is awareness: flag the doors that are hard to walk back through.
## Scope & Adjacent Work
- Fix trivial adjacent issues (<5 lines) inline and mention what you did.- For anything bigger, add it to the task list and stay on task.
## Tests as Feedback
- Tests are the primary feedback tool. Always write tests for meaningful changes — don't reach for one-off scripts or manual verification.- Use tests to validate your own work before surfacing results. Write them early, run them often.
## Error Recovery
- When hitting build/test/type errors mid-task, attempt 2–3 fix cycles before surfacing the issue. Use judgment — if the fix is obvious, keep going; if you're circling, stop and report.
## Git Workflow
- One commit per completed task with a descriptive message.- Use feature branches. Branch naming: `<type>/<short-description>` (e.g. `feat/add-auth`, `fix/connection-pooling`).- Don't commit partial or broken work.
# Agent Work Log
Log completed work with `aw task "description" --category <category>`
## What to Log
Log meaningful completions: features, bug fixes, refactors, config changes, tests, docs, research findings.
**Don't log**: file reading, trivial edits, planning, incomplete work.
## Categories
feature | bugfix | refactor | docs | config | test | perf | infra | security | research
## Examples
```bashaw task "Implemented JWT authentication with refresh tokens" --category featureaw task "Fixed race condition in database connection pooling" --category bugfixaw task "Refactored API routes to use middleware pattern" --category refactor```
<!-- End Agent Work Log -->