# Style guide ## Comments Keep comments minimal, one line if possible. There is one right place to document any given information: - Functions should have a doc comment explaining the purpose of the function, not its implementation. - Only very strange behavior should be documented using non-doc comments. - Models and their fields are documented in models.rs, not in migrations, implementations, or anywhere on the frontend. - Database indexes are documented in migrations. ## Data Modeling When naming a foreign key, always use `{model}_{pk}`, for example `relay.tenant_pubkey`. When referring to a tenant's pubkey, always name it `tenant_pubkey`, not `tenant` or `pubkey`. The exception to this is when we're in a context where we're already talking about tenants, e.g. `tenant.pubkey`, `get_tenant(pubkey)` or any tenant-related routes. ## Migrations Pre-release: squash schema changes into `0001_init.sql` rather than adding new migration files. Once released, migrations become append-only. Document indexes (what use cases they support), but not tables (those are documented in `models.rs`). ## Markdown Do not hard-break markdown files at a certain number of characters. Allow readers to implement line wrapping naturally instead. ## Rust Prefer `&str` over `&String` for function parameters — `&str` accepts both `&String` (via deref coercion) and string literals, so it's strictly more flexible. Only take `String` when you need ownership (storing in a struct, mutating, or transferring ownership). Avoid passing `&mut` to functions. The performance improvement often comes at the cost of poor abstraction boundaries and error prone business logic. Instead, return results to the caller which can manage mutability itself, or re-calculate/fetch mutable data. Don't be overly DRY. Deep call trees are harder to read; factoring functions into many tiny pieces means that function boundaries are defined less by the domain or the responsibility of a given piece of code than by coincidental similarity. New functions should be created when 1. they represent a different concern that is the responsibility of a different part of the codebase, 2. the contained logic is repeated 3+ times, or 3. the contents of the function are complex and naming them makes the logical flow of the code easier to follow. ## Typescript Don't add re-export shims to preserve old import paths. When something moves, import it from its new canonical location at every call site and update all the imports. Each symbol has exactly one place it's exported from. ## Verification Check justfile and frontend/package.json for common commands for linting/building. ## Skills Skills should be created and maintained when updating the codebase. Before creating a skill, check to see if a relevant one already exists. Avoid including code in skills unless the purpose of the skill is to illustrate coding principles; architecture and domain should be explained in plain English and provide a high-level overview of the topic without going into implementation details.