Coding Standards
This document describes the coding standards and conventions used across the ProBeya codebase. Following these ensures consistency and makes code reviews smoother.
Language
The entire codebase is written in TypeScript. We use strict mode ("strict": true in tsconfig) everywhere. Avoid any types — use unknown and type narrowing instead.
Formatting
We use Prettier for code formatting with the following configuration:
{
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"useTabs": false
}
Run pnpm format to format all files. The CI pipeline rejects unformatted code.
Linting
We use ESLint with a custom configuration that extends:
@typescript-eslint/recommended@typescript-eslint/strict-type-checkedeslint-plugin-react-hookseslint-plugin-import
Run pnpm lint to check for issues. Run pnpm lint:fix to auto-fix where possible.
File Structure
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Files | kebab-case | user-profile.ts |
| React components | PascalCase file | BoardView.tsx |
| Directories | kebab-case | custom-fields/ |
| Variables and functions | camelCase | getUserById |
| Types and interfaces | PascalCase | BoardConfig |
| Constants | SCREAMING_SNAKE_CASE | MAX_ITEMS_PER_PAGE |
| Database tables | snake_case | board_items |
| API procedures | camelCase with dot notation | item.create |
Imports
Order imports in this sequence (enforced by ESLint):
- Node built-in modules (
node:fs,node:path) - External packages (
react,drizzle-orm) - Internal packages (
@probeya/db,@probeya/validators) - Relative imports (
./components,../utils)
import { createContext, useContext } from "react";
import { eq } from "drizzle-orm";
import { db } from "@probeya/db";
import { itemSchema } from "@probeya/validators";
import { BoardHeader } from "./board-header";
import { useBoard } from "../hooks/use-board";
React Conventions
- Use function declarations for components, not arrow functions:
export function BoardView({ boardId }: { boardId: string }) {// ...}
- Use named exports, not default exports.
- Co-locate component-specific hooks, types, and utilities in the same directory.
- Use Zustand for client-side state management — avoid prop drilling beyond 2 levels.
API Conventions
- All tRPC procedures must have Zod input validation.
- Use the
protectedProcedurebase for authenticated endpoints. - Return consistent shapes — use the
Resultpattern for operations that can fail. - Log errors with structured context:
logger.error("Failed to create item", { boardId, userId, error });
Database Conventions
- Use Drizzle ORM for all database interactions — no raw SQL in application code.
- Always scope queries to the current organization using the
withOrgmiddleware. - Use transactions for operations that modify multiple tables.
- Prefer
returning()over separate select queries after inserts/updates.
Error Handling
- Use tRPC error codes (
TRPCError) for API errors. - Never expose internal error details to clients.
- Log the full error server-side with context.
- Provide user-friendly error messages in the response.
import { TRPCError } from "@trpc/server";
throw new TRPCError({
code: "NOT_FOUND",
message: "Board not found",
});