name: code-style description: Biome formatting, import style, strict TypeScript, naming (including React file names), or generated files.
Code style, TypeScript, and naming
When to use: Biome formatting, import style, strict TypeScript, naming (including React file names), or generated files. For where domain code lives, see architecture-boundaries.
Code style (Biome)
Biome config (biome.json) is the source of truth:
- Indentation: 2 spaces
- Max line width: 120
- Strings: double quotes
- Semicolons: as needed
- Ignore generated/output paths:
dist/**,coverage/**,.turbo/**,node_modules/**,**/*.gen.ts,**/models.dev.json - Prefer package-local formatting:
pnpm --filter @app/api format
Imports
- Prefer static imports; avoid dynamic import patterns unless justified
- Use
import type { ... }for type-only imports - Keep imports explicit and grep-friendly
- Preserve clear grouping/order (external, internal alias, then relative)
- Avoid wildcard exports/imports when explicit named exports are practical
- Avoid barrel files (
index.tsre-exporting from the same directory); import from the specific module - Use
.ts/.tsxextensions in relative imports (not.js). The codebase uses TypeScript source extensions for module resolution
TypeScript
Base config: tsconfig.base.json. Typechecking runs under tsgo (TypeScript 7 beta, via @typescript/native-preview) — use tsgo -p tsconfig.json --noEmit in typecheck scripts, never tsc.
strict: trueis enabled; keep code strict-clean- Module system:
NodeNext+ ESM ("type": "module"in packages/apps) - For new domain data contracts, define the canonical shared shape as a Zod schema first when runtime validation is required, then infer TypeScript types from that schema or from Drizzle schemas where appropriate.
- Treat schemas in
src/entities/<entity>.tsas the canonical domain contract. Schemas and types elsewhere in the same domain or at app/platform boundaries should derive from or reuse those entity shapes whenever practical instead of restating identical fields. - Canonical entity schemas should treat system-managed fields such as
id,createdAt, andupdatedAtas core entity fields. Do not split an entity into a business payload plus an appended "persistence" wrapper unless there is a truly distinct boundary/input DTO that needs that separation. - Enum-like contracts should use literal-string unions or
as constobjects, not TypeScript enums. - Use shared domain schemas to validate data crossing from app/platform boundaries into domain use-cases.
- If a boundary schema must differ materially from the entity shape, reuse domain constants, field schemas, and literal unions before introducing duplicated inline limits or sentinel values.
- Configurable thresholds, weights, debounce windows, sentinel values, and similar tunables should live in named constants inside the owning domain package rather than as scattered inline literals.
- Types and schemas that exist only as the inputs of one use-case should stay in that use-case file unless several use-cases truly share the same contract.
- Prefer explicit domain types/interfaces over loose objects
- Methods/functions with more than one argument should default to a single named-arguments object rather than positional arguments
- Use
readonlyfields for immutable domain data shapes - Avoid
any; useunknown+ narrowing - Avoid unnecessary type assertions (
as { ... }); prefer relying on inferred types from libraries - Validate boundary inputs early (API input, queue payloads, external IO)
General principles
- Prefer minimal, explicit abstractions — YAGNI
- Comments: default to none, one line when needed — see the repo-wide convention in the root
AGENTS.md. Only traps (code that looks removable or wrong but isn't), non-obvious external constraints, and sibling-complete contract docs (e.g. port JSDoc) earn a comment; never what-narration or design rationale
Domain module layout
- Canonical entity schemas and inferred entity types belong in
src/entities/<entity>.ts. - Domain package constants belong in
src/constants.ts. - Domain package errors belong in
src/errors.ts. - Small domain-scoped shared helpers such as predicates or lifecycle helpers belong in
src/helpers.ts.
Naming conventions
- Types/interfaces/classes:
PascalCase - Variables/functions/methods:
camelCase - Constants:
UPPER_SNAKE_CASEonly for true constants; otherwisecamelCase+as const - Shared base schemas/types that are extended into discriminated unions or related variants should use
baseXxxSchema/BaseXxxnaming rather thanxxxCommonSchema/XxxCommon. - File names favor concise module roots (
src/index.ts,src/server.ts,src/main.tsx) - React component files use kebab-case:
my-component.tsxormy-component/index.tsx— neverPascalCasefile names (e.g.MyComponent.tsx). This matches the@repo/uiconvention (table-skeleton.tsx,form-field.tsx, etc.) - Package names follow scoped workspace style (
@app/*,@domain/*, etc.)
Generated files
apps/web/src/routeTree.gen.tsis auto-generated by TanStack Router — do not manually edit- Generated files may be regenerated during builds or dev server runs; commit them when they change but do not modify by hand