> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/calcom/cal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Coding Guidelines

> Best practices and conventions for Cal.com development

## Core Principles

As a Cal.com contributor, you should prioritize:

1. **Type Safety** - Use TypeScript strictly, avoid `any`
2. **Security** - Never expose sensitive data or credentials
3. **Small PRs** - Keep changes focused and reviewable (\<500 lines, \<10 files)
4. **Code Quality** - Write clean, maintainable code that explains "why", not "what"

## Do's and Don'ts

### Always Do

<Check>Use `select` instead of `include` in Prisma queries</Check>
<Check>Use `import type { X }` for TypeScript type imports</Check>
<Check>Use early returns to reduce nesting</Check>
<Check>Use `ErrorWithCode` for non-tRPC errors, `TRPCError` for tRPC routers</Check>
<Check>Use conventional commits: `feat:`, `fix:`, `refactor:`</Check>
<Check>Import directly from source files, not barrel files</Check>
<Check>Add translations to `packages/i18n/locales/en/common.json`</Check>
<Check>Use `date-fns` or native `Date` when timezone awareness isn't needed</Check>
<Check>Put permission checks in `page.tsx`, never in `layout.tsx`</Check>
<Check>Use Biome for formatting and linting</Check>
<Check>Run `yarn type-check:ci --force` before pushing</Check>

### Never Do

<Warning>Never use `as any` - use proper type-safe solutions</Warning>
<Warning>Never expose `credential.key` field in API responses</Warning>
<Warning>Never commit secrets, API keys, or `.env` files</Warning>
<Warning>Never modify `*.generated.ts` files directly</Warning>
<Warning>Never put business logic in repositories</Warning>
<Warning>Never use barrel imports from index.ts files</Warning>
<Warning>Never skip running type checks before pushing</Warning>
<Warning>Never create large PRs (>500 lines or >10 files)</Warning>
<Warning>Never add comments that restate what code does</Warning>

## TypeScript Best Practices

### Type Imports

```typescript theme={null}
// Good - Type imports
import type { User } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";

// Bad - Regular imports for types
import { User } from "@prisma/client";
```

### Avoid `any`

```typescript theme={null}
// Good - Proper typing
function processBooking(booking: Booking): BookingResponse {
  return { id: booking.id, status: booking.status };
}

// Bad - Using any
function processBooking(booking: any): any {
  return booking;
}
```

### Use Early Returns

```typescript theme={null}
// Good - Early returns reduce nesting
function getBooking(id: string) {
  const booking = await prisma.booking.findUnique({ where: { id } });
  if (!booking) return null;
  
  const user = await prisma.user.findUnique({ where: { id: booking.userId } });
  if (!user) return null;
  
  return { ...booking, user };
}

// Bad - Nested conditions
function getBooking(id: string) {
  const booking = await prisma.booking.findUnique({ where: { id } });
  if (booking) {
    const user = await prisma.user.findUnique({ where: { id: booking.userId } });
    if (user) {
      return { ...booking, user };
    }
  }
  return null;
}
```

## Prisma Best Practices

### Use `select` Over `include`

```typescript theme={null}
// Good - Explicit field selection
const booking = await prisma.booking.findFirst({
  select: {
    id: true,
    title: true,
    startTime: true,
    user: {
      select: {
        id: true,
        name: true,
        email: true,
      },
    },
  },
});

// Bad - Includes all fields (performance and security issue)
const booking = await prisma.booking.findFirst({
  include: {
    user: true,
  },
});
```

<Info>
  Using `select` improves performance and prevents accidental exposure of sensitive data like `credential.key`.
</Info>

### Never Expose Credentials

```typescript theme={null}
// Good - Exclude sensitive fields
const credentials = await prisma.credential.findMany({
  select: {
    id: true,
    type: true,
    userId: true,
    // Never select credential.key
  },
});

// Bad - Exposes sensitive data
const credentials = await prisma.credential.findMany();
```

## Import Best Practices

### Direct Imports, Not Barrel Files

```typescript theme={null}
// Good - Direct imports
import { Button } from "@calcom/ui/components/button";
import { Dialog } from "@calcom/ui/components/dialog";

// Bad - Barrel imports (slower, breaks tree-shaking)
import { Button, Dialog } from "@calcom/ui";
```

### API v2 Imports

When importing into `apps/api/v2`, re-export from platform libraries:

```typescript theme={null}
// Step 1: In packages/platform/libraries/index.ts
export { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";

// Step 2: In apps/api/v2, import from platform-libraries
import { ProfileRepository } from "@calcom/platform-libraries";

// Bad - Direct import causes module not found error
import { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";
```

## Error Handling

### Use Appropriate Error Classes

```typescript theme={null}
// Good - TRPCError in tRPC routers
import { TRPCError } from "@trpc/server";

throw new TRPCError({
  code: "NOT_FOUND",
  message: `Booking ${bookingId} not found for user ${userId}`,
});

// Good - ErrorWithCode in services/utilities
import { ErrorWithCode } from "@calcom/lib/error";

throw new ErrorWithCode(
  `Unable to create booking: User ${userId} has no available time slots for ${date}`,
  "BOOKING_UNAVAILABLE"
);

// Bad - Generic error without context
throw new Error("Booking failed");
```

### Descriptive Error Messages

Always provide context in error messages:

```typescript theme={null}
// Good - Context included
throw new Error(
  `Unable to send email to ${email}: SMTP connection failed with code ${errorCode}`
);

// Bad - No context
throw new Error("Email failed");
```

## File Naming Conventions

### Services

```typescript theme={null}
// Pattern: <Entity>Service.ts

// File: MembershipService.ts
export class MembershipService { ... }

// File: HashedLinkService.ts
export class HashedLinkService { ... }
```

### Repositories

```typescript theme={null}
// Pattern: Prisma<Entity>Repository.ts

// File: PrismaAppRepository.ts
export class PrismaAppRepository { ... }

// File: PrismaMembershipRepository.ts
export class PrismaMembershipRepository { ... }
```

<Note>
  * File names must match exported class names exactly (PascalCase)
  * Avoid dot-suffixes like `.service.ts` or `.repository.ts` (legacy patterns)
  * Reserve suffixes for `.test.ts`, `.spec.ts`, and `.types.ts`
</Note>

## Code Comments

Only add comments that explain **why**, not **what**.

```typescript theme={null}
// Good - Explains why
// We need to delay the webhook by 5 seconds to ensure the booking
// is fully committed before external systems receive the notification
await delay(5000);

// Bad - Restates what the code does
// Get the user
const user = await getUser();
```

## Internationalization

Add all UI strings to translation files:

```typescript theme={null}
// packages/i18n/locales/en/common.json
{
  "booking_confirmed": "Booking confirmed",
  "booking_cancelled": "Booking cancelled"
}

// Usage in components
import { useLocale } from "@calcom/lib/hooks/useLocale";

const { t } = useLocale();
return <div>{t("booking_confirmed")}</div>;
```

## Conventional Commits

Use conventional commit format for PR titles:

```bash theme={null}
feat(bookings): add cancellation reason field
fix(calendar): resolve timezone conversion bug
refactor(auth): extract session validation logic
docs(api): update webhook documentation
test(bookings): add unit tests for reschedule flow
```

**Format:** `<type>(<scope>): <description>`

**Types:**

* `feat` - New feature
* `fix` - Bug fix
* `refactor` - Code refactoring
* `docs` - Documentation changes
* `test` - Adding/updating tests
* `chore` - Maintenance tasks

## PR Size Guidelines

Keep PRs small and focused for faster reviews and easier debugging.

### Size Limits

* **Lines changed:** \<500 lines of code
* **Files changed:** \<10 code files
* **Single responsibility:** Each PR should do one thing well

<Note>
  These limits exclude documentation, lock files, and auto-generated files.
</Note>

### Splitting Large Changes

**By layer:**

```
PR 1: Database schema changes
PR 2: Backend API endpoints
PR 3: Frontend UI components
PR 4: Integration and testing
```

**By feature component:**

```
PR 1: Add notification preferences schema
PR 2: Add notification service and API
PR 3: Add notification UI components
PR 4: Integrate notifications into booking flow
```

**By refactor vs feature:**

```
PR 1: Extract calendar logic into service
PR 2: Add new calendar provider support
```

## Testing Requirements

### Before Committing

```bash theme={null}
# Type check
yarn type-check:ci --force

# Lint and format
yarn biome check --write .

# Run relevant tests
TZ=UTC yarn test
```

### Before Pushing

<Checklist>
  * [ ] Type check passes
  * [ ] Lint passes
  * [ ] Relevant unit tests pass
  * [ ] Relevant E2E tests pass (if applicable)
  * [ ] No secrets or API keys committed
</Checklist>

## Security Guidelines

### Never Commit Secrets

```bash theme={null}
# Add to .gitignore
.env
.env.local
credentials.json
```

### Validate User Input

```typescript theme={null}
import { z } from "zod";

// Good - Zod validation
const createBookingSchema = z.object({
  eventTypeId: z.number(),
  startTime: z.string().datetime(),
  timeZone: z.string(),
});

const validated = createBookingSchema.parse(input);
```

### Sanitize Output

```typescript theme={null}
// Good - Only return necessary fields
return {
  id: user.id,
  name: user.name,
  email: user.email,
  // Never include password, tokens, or credential keys
};
```

## Boundaries

### Ask First

* Adding new dependencies
* Schema changes to `packages/prisma/schema.prisma`
* Changes affecting multiple packages
* Deleting files
* Running full build or E2E suites

### Never Do Without Permission

* Commit secrets or API keys
* Force push to shared branches
* Modify generated files directly
* Expose sensitive data in APIs

## PR Checklist

Before submitting a PR:

<Checklist>
  * [ ] Title follows conventional commits: `feat(scope): description`
  * [ ] Type check passes: `yarn type-check:ci --force`
  * [ ] Lint passes: `yarn lint:fix`
  * [ ] Relevant tests pass
  * [ ] Diff is small and focused (\<500 lines, \<10 files)
  * [ ] No secrets or API keys committed
  * [ ] UI strings added to translation files
  * [ ] Created as draft PR
  * [ ] PR description includes context and testing details
  * [ ] Linked related issues with "Fixes #XXX" or "Closes #XXX"
  * [ ] "Allow edits from maintainers" is checked
</Checklist>

## Code Review Guidelines

### As a Contributor

* Respond to feedback promptly
* Ask questions if feedback is unclear
* Update your branch regularly
* Test suggested changes before pushing

### As a Reviewer

* Be respectful and constructive
* Explain the "why" behind suggestions
* Approve when ready, don't nitpick
* Use "Request changes" sparingly

## Resources

* [AGENTS.md](https://github.com/calcom/cal.com/blob/main/AGENTS.md) - AI development guidelines
* [agents/rules/](https://github.com/calcom/cal.com/tree/main/agents/rules) - Modular engineering rules
* [agents/commands.md](https://github.com/calcom/cal.com/blob/main/agents/commands.md) - Command reference

## Next Steps

* Learn about [Testing Practices](/developers/contributing/testing)
* Review [Development Setup](/developers/contributing/setup)
* Explore [Architecture Overview](/developers/contributing/architecture)
