This is a TypeScript monorepo using Turborepo with Next.js frontends, tRPC APIs, and shared utilities via Prisma. The stack demands strict module boundaries, type safety across package borders, and consistent tooling. AI assistants frequently break workspace imports, scatter types across packages, and create inconsistent configurations.
- File names: use kebab-case for all files except React components
- React component files: PascalCase (Button.tsx, UserProfile.tsx)
- Exported functions: camelCase
- Exported types/interfaces: PascalCase
- Constants: UPPER_SNAKE_CASE only for truly immutable module-level values
- Private functions: prefix with underscore only in classes, never in modules
- Boolean variables/functions: prefix with is, has, should, or can (isLoading, hasError, shouldRetry, canDelete)
- Never use any type. If you cannot type something, use unknown and narrow it
- Never use as type assertions outside of test files
- Use satisfies operator for object literal type checking instead of as const
- All function parameters must have explicit types; no implicit any
- All function return types must be explicit except for arrow functions with single-line returns
- Use const assertions (as const) only in test files or config files
- Generic types must have constraints: never write , always write
- Group imports: React/external libraries first, then workspace packages, then relative imports
- Workspace imports must use @workspace/package-name aliases, never relative paths like ../../../
- Do not import from sibling packages directly; route through shared package
- Sort imports alphabetically within each group
- Remove unused imports before committing
- Use named exports exclusively; no default exports except for Next.js pages and layout files
- Export types separately from implementations: export type { MyType } from './types'
- Never export * (star exports) from index files; list exports explicitly
- Index files in directories must re-export all public APIs from that directory
- apps/web: Next.js frontend application
- apps/api: tRPC backend server (if separate from web)
- packages/shared: TypeScript types, utilities, and constants shared across all packages
- packages/db: Prisma schema and generated client
- packages/api: tRPC router definitions and procedures (if monorepo pattern)
- Each package must have its own tsconfig.json extending base tsconfig.json
- packages/shared/src/types: all exported types used across packages
- packages/shared/src/utils: pure utility functions with no side effects
- packages/shared/src/constants: immutable configuration values
- packages/shared/src/errors: custom error classes (AppError, ValidationError, etc.)
- Never put API routes, React components, or Prisma models in shared
- apps/web/src/pages: Next.js page files only
- apps/web/src/components: React components, organized by feature
- apps/web/src/hooks: custom React hooks
- apps/web/src/lib: client-side utilities and helpers
- apps/web/src/styles: CSS modules or Tailwind config
- Never put server-only code in apps/web/src/lib; use API routes or tRPC
- packages/api/src/routers: tRPC router definitions, one file per domain (users.ts, posts.ts)
- packages/api/src/procedures: reusable tRPC procedure middleware
- packages/api/src/middleware: authentication, logging, error handling
- packages/api/src/context: tRPC context factory function
- Never put business logic directly in routers; extract to services
- packages/db/prisma/schema.prisma: single source of truth for data model
- packages/db/src/client.ts: Prisma client singleton export
- packages/db/src/seeds: seed scripts for development
- Never import Prisma client directly; always import from packages/db
- turbo.json must define pipeline for all tasks: build, dev, test, lint, type-check
- Each package must declare its dependencies in turbo.json outputs
- New tasks added to any package must be registered in turbo.json pipeline
- Cache outputs must be explicitly listed; never rely on implicit caching
- Every tRPC router must use z.object() for input validation; never skip validation
- Input validation must use Zod schemas from packages/shared/src/types
- Router procedures must have explicit return types: .query() or .mutation()
- Never use any in router definitions; type all inputs and outputs
- Procedures must call middleware functions explicitly: .use(isAuthenticated).mutation(...)
- Error responses must throw TRPCError with code and message, never throw raw Error
- Create custom error class: class AppError extends Error { code: string; statusCode: number }
- All async functions must have try/catch blocks
- Caught errors must be typed: catch (err) { if (err instanceof AppError) ... }
- Never catch (e: any) or catch (e: unknown) without type narrowing
- HTTP endpoints must catch errors and return { error: { code, message } } JSON
- tRPC procedures must throw TRPCError, never throw raw Error or AppError
- Validation errors must throw TRPCError with code: 'BAD_REQUEST'
- Database errors must be caught and re-thrown as TRPCError with code: 'INTERNAL_SERVER_ERROR'
- All tRPC inputs must use Zod schemas; never accept unvalidated data
- All HTTP POST/PUT/PATCH bodies must be validated with Zod before processing
- Validation schemas must live in packages/shared/src/types, not in routers
- Use .parse() for validation that should throw; use .safeParse() only in error handlers
- Validation errors must include field-level details: { field: string; message: string }[]
- Create middleware function: const isAuthenticated = t.middleware(({ ctx, next }) => { ... })
- All protected procedures must use .use(isAuthenticated) before .query() or .mutation()
- User ID must come from ctx.userId, never from request headers or cookies directly
- Authorization checks must happen in procedure logic, not in middleware
- Never store secrets in environment variables without NEXT_PUBLIC_ prefix check
- Always import Prisma client from packages/db, never instantiate new PrismaClient()
- Use Prisma transactions for multi-step operations: prisma.$transaction([...])
- Never use raw SQL queries; use Prisma query builder
- Handle Prisma errors explicitly: catch (err) { if (err instanceof Prisma.PrismaClientKnownRequestError) ... }
- Use select: { field: true } to limit returned fields; never return entire models unnecessarily
- Functional components only; no class components
- Use React.FC for typed component signatures
- Props interface must be defined above component: interface ButtonProps { ... }
- Use useCallback for event handlers passed to children
- Use useMemo only when profiler shows performance issue, never preemptively
- Custom hooks must start with use prefix and live in apps/web/src/hooks
- Never import types from apps/web into packages/api
- Never import types from apps/api into packages/shared
- Shared types must be framework-agnostic (no React.FC, no tRPC types)
- Create separate type files for each layer: api.types.ts, ui.types.ts, domain.types.ts
- Use type-only imports: import type { User } from '@workspace/shared'
- Unit tests: tests/unit/[feature].test.ts in same directory as source
- Integration tests: tests/integration/[feature].test.ts
- API tests: tests/api/[router].test.ts for tRPC routers
- Test files must use .test.ts extension, never .spec.ts
- Never put test files in src/ directory
- All utility functions in packages/shared must have unit tests
- All tRPC procedures must have integration tests with mock context
- All Zod schemas must have validation tests covering success and failure cases
- All error classes must have tests verifying error code and message
- React components must have tests for user interactions, not implementation details
- Use describe() blocks to organize tests by feature or function
- Use beforeEach() to set up test fixtures, never duplicate setup code
- Mock external dependencies (Prisma, tRPC context) with jest.mock()
- Never mock internal utility functions; test them directly
- Assertions must be specific: expect(result).toEqual(expected), never expect(result).toBeTruthy()
- Test error cases explicitly: expect(() => fn()).toThrow(AppError)
- Create factory functions for test data: createMockUser(), createMockPost()
- Store factories in tests/factories directory
- Never hardcode test data in test files
- Use meaningful test data values, not lorem ipsum or random strings
Source: Codelibrium — the marketplace for AI behaviour files. Browse multiple rulesets at codelibrium.com or install via CLI:
npx codelibrium-cli install <ruleset-name>