This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
The project uses pnpm workspaces. Key commands:
pnpm build- Build all packages (runs recursive build command)pnpm vitest run- Run all tests with Vitestpnpm vitest run <path>- Run specific test file (e.g.,packages/zod/src/v4/classic/tests/string.test.ts)pnpm vitest run <path> -t "<pattern>"- Run specific test(s) within a file (e.g.,-t "MAC")pnpm vitest run --update- Update all test snapshotspnpm vitest run <path> --update- Update snapshots for specific test filepnpm test:watch- Run tests in watch modepnpm vitest run --coverage- Run tests with coverage reportpnpm dev- Execute code with tsx under source conditionspnpm dev <file>- Execute<file>with tsx & proper resolution conditions. Usually use forplay.ts.pnpm dev:play- Quick alias to run play.ts for experimentationpnpm lint- Run biome linter with auto-fixpnpm format- Format code with biomepnpm fix- Run both format and lint
Code is clean if it can be understood easily – by everyone on the team. Clean code can be read and enhanced by a developer other than its original author. With understandability comes readability, changeability, extensibility and maintainability.
- Follow standard conventions.
- Keep it simple stupid. Simpler is always better. Reduce complexity as much as possible.
- Boy scout rule. Leave the campground cleaner than you found it.
- Always find root cause. Always look for the root cause of a problem.
// ❌ Overly complex
if (user) {
if (user.isActive) {
if (user.email?.includes('@')) {
return sendEmail(user.email);
}
}
}
// ✅ Simple and clear
if (!user?.isActive || !user?.email?.includes('@')) return null;
return sendEmail(user.email);- Keep configurable data at high levels.
- Prefer polymorphism to if/else or switch/case.
- Use dependency injection.
- Follow Law of Demeter. A class should know only its direct dependencies.
// ❌ Switch for payment types
function processPayment(method: string, amount: number) {
switch (method) {
case 'stripe': return processStripe(amount);
case 'paypal': return processPayPal(amount);
}
}
// ✅ Polymorphism with dependency injection
interface PaymentProcessor {
process(amount: number): Promise<PaymentResult>;
}
class PaymentService {
constructor(private processor: PaymentProcessor) {}
async process(amount: number) {
return this.processor.process(amount);
}
}- Be consistent. If you do something a certain way, do all similar things in the same way.
- Use explanatory variables.
- Prefer dedicated value objects to primitive types.
- Avoid negative conditionals.
// ❌ Complex condition
if (user.age >= 18 && user.hasVerifiedEmail && user.subscription.status === 'active') {
allowAccess();
}
// ✅ Explanatory variables
const canAccessPremiumContent =
user.age >= 18 &&
user.hasVerifiedEmail &&
user.subscription.status === 'active';
if (canAccessPremiumContent) {
allowAccess();
}// ❌ Primitives everywhere
function createUser(email: string, age: number) {
if (!email.includes('@')) throw new Error('Invalid email');
if (age < 0 || age > 150) throw new Error('Invalid age');
}
// ✅ Value objects
class Email {
private constructor(private readonly value: string) {
if (!value.includes('@')) throw new Error('Invalid email');
}
static create(value: string) { return new Email(value); }
toString() { return this.value; }
}
function createUser(email: Email, age: Age) {
// Validation already done!
}- Choose descriptive and unambiguous names.
- Use pronounceable names.
- Use searchable names.
- Replace magic numbers with named constants.
- Avoid encodings. Don't append prefixes or type information.
// ❌ Bad names
const yyyymmdd = new Date().toISOString().split('T')[0];
function calc(a: number, b: number) { return a * 1.2 + b; }
// ✅ Good names
const currentDate = new Date().toISOString().split('T')[0];
const TAX_RATE = 1.2;
function calculateTotalWithTax(basePrice: number, shipping: number) {
return basePrice * TAX_RATE + shipping;
}- Small.
- Do one thing.
- Use descriptive names.
- Prefer fewer arguments (use objects for 3+).
- Have no side effects.
- Don't use flag arguments. Split into separate methods.
// ❌ Flag argument
function saveUser(user: User, sendEmail: boolean) {
db.save(user);
if (sendEmail) sendWelcomeEmail(user.email);
}
saveUser(user, true); // What does true mean?
// ✅ Separate methods
function saveUser(user: User) {
db.save(user);
}
function saveUserAndNotify(user: User) {
saveUser(user);
sendWelcomeEmail(user.email);
}// ❌ Too many arguments
function createPost(title: string, content: string, authorId: string,
categoryId: string, tags: string[], isDraft: boolean) { }
// ✅ Object parameter
interface CreatePostParams {
title: string;
content: string;
authorId: string;
categoryId: string;
tags: string[];
isDraft: boolean;
}
function createPost(params: CreatePostParams) { }- Always try to explain yourself in code.
- Don't be redundant.
- Don't comment out code. Just remove.
- Use as explanation of intent.
- Use as warning of consequences.
// ❌ Redundant
const userName = user.name; // Get the user name
// ✅ Good: Code explains itself
const userName = user.name;
// ✅ Good: Explaining intent
// Using Set for O(1) lookup performance with large datasets
const processedIds = new Set<string>();
// ✅ Good: Warning
// WARNING: Deletes all user data permanently. Cannot be undone.
async function deleteAllUsers() {
await db.query('TRUNCATE TABLE users CASCADE');
}// ❌ Wrong: Comment explains complex condition
if (config.allowClose && config.interactWithActiveElement && event.key === 'Escape') {
close(); // popover escape disabled when interactWithActiveElement is true
}
// ✅ Fix: Variable explains intent
const escapePopoverFeatureDisabled = config.interactWithActiveElement;
if (config.allowClose && escapePopoverFeatureDisabled && event.key === 'Escape') {
close();
}- Separate concepts vertically.
- Declare variables close to their usage.
- Dependent functions should be close.
- Keep lines short.
- Use whitespace to associate related things.
// ❌ Variables far from usage
function processOrder(order: Order) {
const userName = order.user.name;
const userEmail = order.user.email;
// ... 50 lines of code ...
sendInvoice(userEmail, userName);
}
// ✅ Declare close to usage
function processOrder(order: Order) {
// ... 50 lines of code ...
const userName = order.user.name;
const userEmail = order.user.email;
sendInvoice(userEmail, userName);
}- Hide internal structure.
- Prefer data structures for simple data, objects for behavior.
- Should be small and do one thing.
- Prefer non-static methods to static methods.
// ❌ Exposing internals
class User {
public passwordHash: string;
public internalId: string;
}
// ✅ Hide implementation
class User {
private passwordHash: string;
private internalId: string;
verifyPassword(password: string): boolean {
return bcrypt.compareSync(password, this.passwordHash);
}
}// ❌ Static method requiring dependency every time
class UserService {
static async getUser(db: Database, id: string) {
return db.query('SELECT * FROM users WHERE id = $1', [id]);
}
}
await UserService.getUser(db, '123');
// ✅ Instance method with injected dependency
class UserService {
constructor(private db: Database) {}
async getUser(id: string) {
return this.db.query('SELECT * FROM users WHERE id = $1', [id]);
}
}- One assert per test (one concept per test).
- Readable with clear intent.
- Fast - mock external dependencies.
- Independent - no shared state between tests.
- Repeatable - don't depend on dates or external APIs.
// ❌ Multiple concepts in one test
test('user operations', () => {
const user = createUser({ name: 'John' });
expect(user.name).toBe('John');
const updated = updateUser(user, { name: 'Jane' });
expect(updated.name).toBe('Jane');
});
// ✅ One concept per test
test('should create user with correct name', () => {
const user = createUser({ name: 'John' });
expect(user.name).toBe('John');
});
test('should update user name', () => {
const user = createUser({ name: 'John' });
const updated = updateUser(user, { name: 'Jane' });
expect(updated.name).toBe('Jane');
});- Rigidity - Small change causes cascade of changes
- Fragility - Software breaks in many places from single change
- Needless Complexity - Don't add features you don't need (YAGNI)
- Needless Repetition - Extract common logic (DRY)
- Opacity - Code is hard to understand
// ❌ Rigidity: Changing User breaks multiple places
class UserDisplay {
show(user: { name: string; email: string; age: number }) { }
}
class UserForm {
submit(name: string, email: string, age: number) { }
}
// Adding phone requires changing both classes!
// ✅ Use interface as single source of truth
interface User {
name: string;
email: string;
age: number;
phone?: string; // Easy to extend
}
class UserDisplay {
show(user: User) { }
}
class UserForm {
submit(user: User) { }
}// ❌ Needless Repetition (DRY violation)
async function createUser(data: UserData) {
if (!data.email.includes('@')) throw new Error('Invalid email');
if (data.age < 18) throw new Error('Must be 18+');
return db.insert('users', data);
}
async function updateUser(id: string, data: Partial<UserData>) {
if (data.email && !data.email.includes('@')) throw new Error('Invalid email');
if (data.age && data.age < 18) throw new Error('Must be 18+');
return db.update('users', id, data);
}
// ✅ Extract common validation
function validateEmail(email: string) {
if (!email.includes('@')) throw new Error('Invalid email');
}
function validateAge(age: number) {
if (age < 18) throw new Error('Must be 18+');
}
async function createUser(data: UserData) {
validateEmail(data.email);
validateAge(data.age);
return db.insert('users', data);
}
async function updateUser(id: string, data: Partial<UserData>) {
if (data.email) validateEmail(data.email);
if (data.age) validateAge(data.age);
return db.update('users', id, data);
}