Code Style
Code Style
Consistent code style across the monorepo ensures readability and maintainability.
Formatting
All projects use the same base formatting rules:
| Setting | Value |
|---|---|
| Indentation | Tabs (displayed as 2 spaces) |
| Quotes | Single quotes |
| Line width | 100 characters |
| Semicolons | Yes |
| Trailing commas | ES5 style |
Prettier Configuration
{ "useTabs": true, "tabWidth": 2, "singleQuote": true, "printWidth": 100, "semi": true, "trailingComma": "es5"}Naming Conventions
Files and Directories
| Type | Convention | Example |
|---|---|---|
| Components | PascalCase | UserProfile.tsx, Button.svelte |
| Utilities | camelCase | formatDate.ts, cn.ts |
| Types | camelCase | user.types.ts |
| Constants | SCREAMING_SNAKE | constants.ts (values inside) |
| Test files | *.spec.ts or *.test.ts | users.service.spec.ts |
Code
// Classes - PascalCaseclass UserService {}
// Interfaces - PascalCase, no "I" prefixinterface User {}interface CreateUserDto {}
// Types - PascalCasetype UserRole = 'admin' | 'user';
// Functions - camelCasefunction getUserById(id: string) {}
// Variables - camelCaseconst currentUser = null;
// Constants - SCREAMING_SNAKE_CASEconst MAX_RETRIES = 3;const API_BASE_URL = '/api';
// Enums - PascalCase (values too)enum UserStatus { Active = 'active', Inactive = 'inactive',}<script lang="ts"> // Props - camelCase let { userName, onUpdate }: Props = $props();
// State - camelCase let isLoading = $state(false); let userData = $state<User | null>(null);
// Derived - camelCase let fullName = $derived(`${userData?.first} ${userData?.last}`);
// Functions - camelCase function handleSubmit() {}</script>
<!-- Events - on:eventname (lowercase) --><button onclick={handleSubmit}>Submit</button>// Components - PascalCasefunction UserProfile({ user }: Props) { // Hooks - use prefix const [isLoading, setIsLoading] = useState(false); const queryClient = useQueryClient();
// Handlers - handle prefix const handlePress = () => {};
return <View />;}
// Styles - camelCaseconst styles = StyleSheet.create({ container: {}, headerText: {},});TypeScript Guidelines
Strict Mode
All projects use TypeScript strict mode:
{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "noUnusedLocals": true, "noUnusedParameters": true }}Type Annotations
// Prefer explicit return types for public functionsfunction getUser(id: string): Promise<User | null> { // ...}
// Use inference for simple casesconst users = []; // Inferred as never[], be explicit!const users: User[] = []; // Better
// Prefer interfaces for objectsinterface User { id: string; name: string;}
// Use types for unions/intersectionstype UserRole = 'admin' | 'user' | 'guest';type UserWithRole = User & { role: UserRole };Avoid any
// Badfunction processData(data: any) {}
// Goodfunction processData<T>(data: T) {}function processData(data: unknown) {}function processData(data: Record<string, unknown>) {}Import Organization
Organize imports in this order:
// 1. Node.js built-insimport { readFile } from 'fs/promises';
// 2. External packagesimport { Injectable } from '@nestjs/common';import { eq } from 'drizzle-orm';
// 3. Monorepo packagesimport { JwtAuthGuard } from '@manacore/shared-nestjs-auth';
// 4. Relative imports - parent directoriesimport { AppModule } from '../app.module';
// 5. Relative imports - same directoryimport { UsersService } from './users.service';import { CreateUserDto } from './dto/create-user.dto';Comments
When to Comment
// Good - explains WHY, not WHAT// We retry 3 times because the external API has occasional timeoutsconst MAX_RETRIES = 3;
// Bad - explains WHAT (obvious from code)// Set max retries to 3const MAX_RETRIES = 3;JSDoc for Public APIs
/** * Fetches a user by their ID. * * @param id - The user's UUID * @returns The user object or null if not found * @throws {UnauthorizedException} If the caller lacks permission */async function getUserById(id: string): Promise<User | null> { // ...}TODO Comments
// TODO: Implement pagination (issue #123)// FIXME: This breaks when user.name is null// HACK: Workaround for library bug, remove after v2.0Error Handling
Use Result Types
type Result<T, E = Error> = | { ok: true; value: T } | { ok: false; error: E };
// Usageasync function findUser(id: string): Promise<Result<User, 'NOT_FOUND'>> { const user = await db.users.find(id); if (!user) { return { ok: false, error: 'NOT_FOUND' }; } return { ok: true, value: user };}Throw Only for Exceptional Cases
// Good - expected case, use Resultconst result = await findUser(id);if (!result.ok) { return res.status(404).json({ error: 'User not found' });}
// Good - truly exceptional, throwif (!process.env.DATABASE_URL) { throw new Error('DATABASE_URL is required');}Best Practices
Early Returns
// Bad - nestedfunction processUser(user: User | null) { if (user) { if (user.isActive) { if (user.hasPermission) { // actual logic } } }}
// Good - early returnsfunction processUser(user: User | null) { if (!user) return; if (!user.isActive) return; if (!user.hasPermission) return;
// actual logic}