# MeshiTrack Coding Style & Engineering Guidelines This document establishes the official coding standards, architectural paradigms, and TypeScript guidelines for the MeshiTrack monorepo. These guidelines are designed to enforce clean, testable, object-oriented codebases with pristine type-safety under strict quality gates. --- ## 🏛️ 1. Encapsulation & Class Member Accessibility To ensure clear visibility boundaries, clean interfaces, and easy maintainability: 1. **Mandatory Accessibility Modifiers**: Every field, constructor, and method in a class must explicitly declare its accessibility modifier (`public`, `protected`, or `private`). 2. **Mandate Explicit Fields**: Classes that participate in dependency injection (e.g. Services, Repositories, Providers) must explicitly declare their properties at the class level rather than relying on TypeScript constructor parameter properties. 3. **Class Member Ordering**: Group class members in a predictable sequence: * Static Fields (Public -> Protected -> Private) * Instance Fields (Public -> Protected -> Private) * Constructor * Public Methods * Protected Methods * Private Methods ### Example: ```typescript export class ShoppingListsService { // 1. Explicit, readonly instance properties private readonly shoppingListsRepository: ShoppingListsRepository; // 2. Explicit constructor receiving DI payload public constructor({ shoppingListsRepository }: Deps) { this.shoppingListsRepository = shoppingListsRepository; } // 3. Public class methods public async list(householdId: string): Promise { return this.shoppingListsRepository.list(householdId); } } ``` --- ## 🛡️ 2. Boundary Types & Return Types To prevent type-drift across software boundaries: 1. **Explicit Return Types on Boundary Functions**: All public methods of class instances (Services, Repositories, Providers) and all exported module functions must explicitly declare their return types. 2. **Relegate Inference to Callbacks**: Local, inline variables and anonymous arrow callbacks (e.g. within array map/filter functions) may utilize automatic type inference for conciseness. ### Example: ```typescript // 🟢 Good: Explicitly defined return type on the boundary method public async list(householdId: string): Promise { const lists = await this.shoppingListsRepository.list(householdId); return lists.filter(item => item.status === 'active'); // Inline inference is okay } ``` --- ## 🚫 3. Eradicating `any` & Enforcing Type-Safety TypeScript's `any` type compromises the integrity of our codebase and makes refactoring fragile. 1. **No Explicit `any`**: The use of explicit `any` is strictly prohibited throughout the codebase, including in test files. 2. **Restrict Unsafe Operations**: We enforce strict type-checked rules to prevent the propagation of `any`: * Assigning an `any` to a variable/property is blocked (`@typescript-eslint/no-unsafe-assignment`). * Reading a property off of an `any` is blocked (`@typescript-eslint/no-unsafe-member-access`). * Invoking functions typed as `any` is blocked (`@typescript-eslint/no-unsafe-call`). * Returning an `any` from a function is blocked (`@typescript-eslint/no-unsafe-return`). 3. **Prefer `unknown` for Dynamic Inputs**: Any unchecked external input (e.g., API payloads, database responses, dynamic JSON files) must be typed as `unknown` and validated at the boundary using **Zod schemas** or type guards. 4. **Type-Safe Mocking**: Instead of casting mocks with `as any`, use highly targeted partial types or precise mocks. --- ## 🏷️ 4. Naming Conventions & File Structures Uniform naming conventions prevent conflicts and reduce cognitive load: 1. **Directories**: Lowercase `kebab-case` (e.g., `shopping-lists/`). 2. **Files**: `PascalCase` matching the primary class or component exported in the file (e.g., `ShoppingListsService.ts`), ensuring class name and file name are aligned. 3. **Identifiers**: * **PascalCase**: Classes, Interfaces, Type Aliases, Enums. * **camelCase**: Properties, Methods, Variables, Parameters, Functions. * **UPPER_CASE**: Top-level static read-only constants. 4. **Prohibit Hungarian Notation**: Do not prefix interfaces with `I` (e.g. write `ShoppingListsRepository`, NOT `IShoppingListsRepository`). --- ## 🛑 5. Structured Domain Exceptions Exception mapping must be uniform, semantic, and highly testable: 1. **Prohibit Generic `Error` Throwing**: The service and business layers must never throw generic `new Error('message')` for operational domain failures. 2. **Domain-Specific Exceptions**: Throw subclasses of `AppError` (e.g. `NotFoundError`, `ConflictError`, `BadRequestError`) containing semantic properties like status codes and optional structured sub-details. 3. **Prevent Stack Leakage**: Our custom exceptions map directly to HTTP responses in central middleware, ensuring internal database schemas or runtime details are never leaked to external clients. ### Example: ```typescript // 🟢 Good: Highly testable, structured domain-level exception if (!updated) { throw new NotFoundError('Shopping list not found'); } ``` --- ## 🧱 6. OOP vs. Functional Boundaries We keep class-based OOP strictly segregated from pure stateless helpers: 1. **Stateful Layers -> Classes**: Services, Repositories, and Providers must be class-based to properly encapsulate state, DI, and persistence hooks. 2. **Stateless Operations -> Standalone Pure Functions**: Math utilities, date/string formatters, and mapping helpers must be written as standalone, exported functions in pure modules. 3. **No Utility Classes**: Do not write static utility classes (classes composed solely of static helper methods). Use direct ES module exports. --- ## ⚡ 7. Asynchronous Programming Integrity To prevent background crashes, memory leaks, and silent failures: 1. **Mandate `async/await`**: Use `async/await` exclusively instead of Promise chains (`.then()`, `.catch()`). 2. **Block Floating Promises**: Every Promise must be `await`ed or returned. 3. **Explicit Voiding for Intentional Background Tasks**: If a task must run in the background (fire-and-forget), it must be explicitly voided (e.g. `void this.trackEvent()`) and handle its exceptions internally to prevent uncaught runtime rejections.