Skip to content

Instantly share code, notes, and snippets.

@ifindev
Created March 13, 2026 10:14
Show Gist options
  • Select an option

  • Save ifindev/fd86946267a52f0d10e0f6623c44230e to your computer and use it in GitHub Desktop.

Select an option

Save ifindev/fd86946267a52f0d10e0f6623c44230e to your computer and use it in GitHub Desktop.
Contoh CLAUDE.md

CLAUDE.md

Project Overview

This is a multi-tenant SaaS platform for futsal field management. Each tenant is a business owner who manages one or more futsal venues — handling bookings, schedules, pricing, members, and payments from a single dashboard.

Tech Stack

  • Framework: Next.js 14 (App Router)
  • API Layer: tRPC v11 (end-to-end typesafe)
  • Database: Supabase (PostgreSQL) + Prisma ORM
  • Auth: Supabase Auth (email + OAuth)
  • Styling: Tailwind CSS + shadcn/ui
  • Language: TypeScript (strict mode, no any)
  • Package Manager: pnpm

Architecture

  • Multi-tenant: single database, tenant-scoped queries via tenantId foreign key
  • App Router with route groups: (public), (auth), (dashboard)
  • tRPC routers split by domain: tenant, field, booking, member, payment
  • Prisma as single source of truth for schema — all DB changes go through migrations

Key Directories

src/
├── app/                  # Next.js App Router pages & layouts
│   ├── (public)/         # Landing, registration, public booking
│   ├── (auth)/           # Login, signup, forgot password
│   └── (dashboard)/      # Tenant dashboard (protected)
├── server/
│   ├── routers/          # tRPC routers (one per domain)
│   ├── trpc.ts           # tRPC init, context, middleware
│   └── db.ts             # Prisma client singleton
├── components/
│   ├── ui/               # shadcn/ui primitives (do NOT edit)
│   └── [domain]/         # Domain-specific components
├── lib/                  # Shared utilities, constants, types
└── prisma/
    ├── schema.prisma     # Single schema file
    └── migrations/       # Prisma migrations (auto-generated)

Core Principles

These rules are non-negotiable. Follow them in every task, no exceptions.

  1. Never modify existing tests unless the ticket explicitly asks for it
  2. Never install new packages without asking first — suggest the package and wait for approval
  3. If requirements are unclear, ask before implementing — do not assume or guess intent
  4. Never edit components/ui/ — these are shadcn/ui primitives managed via CLI
  5. Never push directly to main — always work on a feature branch
  6. Never skip migrations — all schema changes must go through prisma migrate dev
  7. Always scope queries by tenantId — unscoped queries are security vulnerabilities
  8. No any types — use unknown + type narrowing if the type is genuinely uncertain
  9. Do not combine unrelated changes in the same commit or PR
  10. Read the full ticket before writing any code — including comments from previous tickets

Code Style & Conventions

General

  • All code in TypeScript strict mode — no as casts unless absolutely necessary (add a // SAFETY: comment explaining why)
  • Prefer const over let, never use var
  • Use early returns to avoid deep nesting
  • Max function length ~50 lines — extract helpers if longer
  • File names: kebab-case.ts for utilities, PascalCase.tsx for components

Naming

  • Variables/functions: camelCase
  • Types/interfaces: PascalCase (no I prefix)
  • Constants: UPPER_SNAKE_CASE
  • Enums: PascalCase enum name, UPPER_SNAKE_CASE values
  • Database columns: camelCase in Prisma schema (maps to snake_case in SQL via @map)
  • tRPC routers: named by domain — tenantRouter, bookingRouter, etc.
  • tRPC procedures: verb + NouncreateBooking, getField, listMembers

Imports

  • Use path aliases: @/server/..., @/components/..., @/lib/...
  • Group imports in order: (1) external packages, (2) @/server, (3) @/components, (4) @/lib, (5) relative imports
  • No barrel exports (index.ts re-exports) — import directly from the source file

Error Handling

  • tRPC procedures: throw TRPCError with appropriate code (NOT_FOUND, FORBIDDEN, BAD_REQUEST, etc.)
  • Never swallow errors silently — always log or re-throw
  • Use Zod for all input validation on tRPC procedures — no manual parsing

Database & Schema Conventions

Prisma Schema

  • Single schema.prisma file — do not split into multiple files
  • Every model MUST have:
    • id — UUID, @default(uuid())
    • createdAtDateTime @default(now())
    • updatedAtDateTime @updatedAt
  • Tenant-scoped models MUST have tenantId String with @relation to Tenant
  • Use @map("snake_case") for column names, @@map("snake_case_plural") for table names
  • Enum values in UPPER_SNAKE_CASE

Migrations

  • Always run prisma migrate dev --name descriptive-name after schema changes
  • Never manually edit migration SQL files unless fixing a known issue
  • Migration names: kebab-case descriptive — e.g. add-booking-status-enum, create-payment-table

Query Patterns

  • Always filter by tenantId — use a middleware or helper that injects it automatically
  • Use select or include explicitly — never fetch entire models when you only need a few fields
  • Paginate list queries — default page size 20, max 100
  • Use transactions (prisma.$transaction) for multi-step writes

Testing Guidelines

API Testing (tRPC procedures)

  • Every ticket with tRPC procedures includes an "API Tests" section with curl commands
  • Run ALL listed test cases against the running dev server (pnpm dev)
  • Post results as a Linear comment with ✅/❌ per test case before marking the ticket as Done

Test Expectations

  • Happy path: valid input → expected output shape and data
  • Validation: invalid/missing fields → BAD_REQUEST error with descriptive message
  • Auth: unauthenticated requests → UNAUTHORIZED
  • Tenant isolation: accessing another tenant's data → FORBIDDEN or NOT_FOUND
  • Edge cases: duplicate entries, empty lists, boundary values

When Writing Tests

  • Test files live next to the source: booking-router.tsbooking-router.test.ts
  • Use descriptive test names: "should return 400 when booking date is in the past"
  • One assertion per test when possible — separate the "what" being tested
  • Mock external services (Supabase Auth, payments) — never hit real APIs in tests

UI/UX Conventions

Component Patterns

  • Use shadcn/ui as the base — import from @/components/ui/
  • Domain components go in @/components/[domain]/ — e.g. @/components/booking/BookingCard.tsx
  • Page-level components go in the route's _components/ folder if only used in that page
  • Always type props with an explicit interface — no inline { } types in function params

Styling

  • Tailwind utility classes only — no custom CSS files unless absolutely necessary
  • Use cn() helper (from @/lib/utils) for conditional class merging
  • Follow shadcn/ui color tokens — primary, secondary, muted, destructive, etc.
  • Responsive: mobile-first — start with base styles, add sm:, md:, lg: breakpoints
  • No hardcoded colors — always use Tailwind theme tokens

Forms

  • Use react-hook-form + zod resolver for all forms
  • Share Zod schemas between tRPC input validation and form validation when possible
  • Always show validation errors inline below the field
  • Disable submit button while submitting, show loading state

Data Fetching

  • Use tRPC's React Query hooks — trpc.booking.list.useQuery(), etc.
  • Show loading skeletons (not spinners) for initial data loads
  • Show inline error states with retry option on failure
  • Optimistic updates for quick actions (toggle, delete) — rollback on error

Linear Workflow

Before starting any ticket

  1. Pull the ticket from Linear: use mcp__linear-server__get_issue with the issue ID
  2. Read comments on the immediately preceding ticket (e.g. if starting E3-003, read E3-002 comments) using mcp__linear-server__list_comments — look for handoff notes left by the previous agent
  3. Read the full description, acceptance criteria, technical notes, and API tests
  4. Set the ticket status to In Progress via mcp__linear-server__save_issue

Branch naming

  • Always create a new branch before starting any implementation
  • Branch name format: {ticket-id}/{short-description}
    • Example: ifi-15/tenant-schema, ifi-16/tenant-registration-form
  • If the current ticket depends on a previous ticket that is not yet merged, branch off that ticket's branch instead of main
    • Check the ticket's "Blocked by" field in Linear to determine the parent branch
  • Otherwise always branch from main

Committing

After implementation is verified (build passes, API tests pass):

  • Use atomic commits: group logically related files into one commit — do not commit file-by-file, but also do not dump all changes in a single commit if they span unrelated concerns
  • Follow Conventional Commits format:
    • feat(scope): description — new feature or endpoint
    • fix(scope): description — bug fix
    • refactor(scope): description — code restructure without behavior change
    • chore(scope): description — tooling, deps, config
    • docs(scope): description — documentation only
    • Scope is optional but preferred (e.g. feat(tenant): add registration form)
  • Keep commit messages concise — describe what changed and why, not how
  • Example groupings for a schema + API + UI ticket:
    • Commit 1: feat(schema): add Tenant and TenantStatusLog models with migration
    • Commit 2: feat(tenant): add tenant registration tRPC procedure
    • Commit 3: feat(ui): add public tenant registration form

While working

  • Add implementation notes as comments via mcp__linear-server__save_comment
  • If a blocker is discovered, add a comment with the blocker details and leave status as In Progress

API testing requirement

  • For every ticket that introduces tRPC procedures, run ALL curl test cases specified in the ticket's "API Tests" section
  • Test the running dev server (pnpm dev) — do not skip tests
  • Post test results (pass/fail per case) as a comment on the ticket before marking Done

After completing a ticket

  1. Run all API tests listed in the ticket and post results as a comment

  2. Post a completion comment using this structure:

    What was built

    • {Bullet list of features/endpoints/UI pages implemented}

    Key files changed

    • {file path} — {what changed}

    API test results

    • ✅ {test case}: passed — {actual response summary}
    • ❌ {test case}: failed — {error}

    Handoff notes

    • {Context the next ticket needs: schema fields added, helpers introduced, patterns to follow}
    • {Any deviations from the original spec and why}

    Formatting rule: wrap all code references in backticks — enum values, field names, model names, function names, status constants, file paths, and CLI commands.

  3. Set status to In Review (needs human review) or Done (self-contained + tests pass)

  4. Create atomic commits following Conventional Commits (see "Committing" section)

  5. Push the branch and open a GitHub PR to main (see "Creating a PR" section)

  6. Post the PR URL as a follow-up comment on the Linear ticket

Finding your next ticket

  • Work in epic dependency order: E1 → E2 → E3/E4/E7 (parallel) → E5 → E6
  • Within an epic, work tickets in numeric order unless a specific one is unblocked earlier
  • Use mcp__linear-server__list_issues filtered by project + status=Todo to find next work
  • Pull the full ticket with mcp__linear-server__get_issue before starting — never work from memory alone

Status meanings

Status Meaning
Backlog Not yet scheduled
Todo Scheduled for current session
In Progress Actively being worked on
In Review Built, awaiting review / testing
Done Verified complete — all AC checked, API tests passed
Canceled Descoped or not needed

GitHub Workflow

Creating a PR

After all commits are pushed, open a GitHub PR targeting main:

Title format: [{TICKET-ID}] {ticket title}

  • Example: [IFI-15] Database Schema — Core Tables (Tenants, Users, Sessions)

PR description format:

## Overview
{1–2 sentences describing what this PR does and why}

- {Bullet expanding on a key detail, decision, or scope point}
- {Another notable aspect — e.g. what was removed, replaced, or why an approach was chosen}

## Changes
- `{file or area}` — {what changed}
- ...

## Testing
- {Step-by-step instructions to verify the changes work}
- {Include any seed data, env vars, or setup needed}

## Notes
{Optional paragraph for deviations from spec, caveats, or anything the reviewer should know.
Omit this section if there is nothing notable.}

## Linear
{Linear ticket URL}
  • After the PR is created, post its URL as a separate comment on the Linear ticket
  • Use mcp__linear-server__save_comment with the PR URL and a one-line summary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment