file naming
why
file names are the symbol table humans and agents search before opening anything: grep, tree, and go-to-definition all resolve through the basename. a vague name forces every lookup to open the file to learn what it owns; two vague names invite duplicate owners of one concept. an exact, granular basename keeps the workspace navigable without reading it.
rule
the basename alone must state the file's primary concept and required responsibility — no opening the file, no relying on its directory. one authored file owns one granular concept, behavior, rule, or outcome, in the shortest exact project-native words.
naming shape
- data or schema: concept noun —
processSnapshot.ts,processSnapshotSchema.ts. - boundary: exact responsibility suffix —
billingChargeMapper.ts,processRouter.ts,processRepository.ts,structuredLogger.ts. - public operation file named for its primary exported operation when that is the only responsibility:
readWorkflowStep.ts. - standard document named as a rule or required outcome:
query-keys-have-one-owner.mdx, notquery-key-ownership.mdx. - procedure: action and result. case study: decision and result.
- authored asset: subject and use; never template names like
file.svgorwindow.svg. - tool-required basenames stay only where the tool mandates them (
page.tsx,route.ts,layout.tsx,package.json, migrations, generated artifacts) — keep them thin adapters or manifests; move real implementation into explicitly named files.
consistency
choose casing by responsibility, then apply it consistently:
| casing | owns |
|---|---|
PascalCase | Authored React components, contexts, and providers: WorkflowPanel.tsx, ProcessContext.tsx, ProcessProvider.tsx. |
camelCase | Authored TypeScript behavior, hooks, schemas, mappers, routes, repositories, and services: readWorkflowStep.ts, useWorkflowTree.ts, processSnapshotSchema.ts. |
snake_case | database tables, columns, constraints, and SQL-owned migration identifiers: workflow_step, process_id. |
kebab-case | shadcn/ui component filenames required by that ecosystem: alert-dialog.tsx, dropdown-menu.tsx. |
never derive a filename by casing or rewriting a display label; store the canonical technical name directly. the exported symbol uses the same semantic words, no second alias.
selection process
- find the canonical domain noun in the glossary.
- name the file's one primary responsibility: render, read, map, validate, persist, route, integrate, enforce, or prove.
- apply the casing required by that responsibility and ecosystem boundary.
- compare the file name with its primary export — same exact concept, no alias.
- read the basename without its path; it must still state concept and responsibility.
- search for an existing file owning the same noun/responsibility pair before creating another.
verification
- read basenames without paths; each should answer what the file owns and what it requires or does.
- each exported public symbol should be discoverable through the same words.
- after a rename, search for generic buckets and retired names; move or delete them so they do not become alternate navigation paths.
good and bad
bad
utils.tshelpers.tsprocessstuff.ts // casing-rewritten display labeldatabase-design.mdx // topic only; no required direction
generic buckets and casing-derived names hide ownership, so tree search fails. "where is normalizeDate used?" means opening utils.ts, helpers.ts, and common.ts in turn instead of finding dateUtils.ts immediately. a single utils.ts sprawls to 500 lines into a junk drawer. "show me what schema validation exists" returns nothing because schemas hide in files with no naming clue.
good
processSnapshotSchema.ts // concept noun, camelCase behaviorbillingChargeMapper.ts // exact responsibility suffixWorkflowPanel.tsx // PascalCase authored componentquery-keys-have-one-owner.mdx // subject plus required direction
each name states one concept with responsibility-chosen casing, so tree reveals the concepts: searching "schema" finds processSnapshotSchema.ts immediately; find . -name "*Mapper*" lists every mapper; readUserPreferences.ts and readWorkflowProgress.ts answer "what data does this service read" unopened.
do not
- use
utils,helpers,common,types,constants,manager,service, ordataas a catch-all authored filename.index.tsis reserved solely for a pure folder barrel under the nesting structure rule. - name a file after a vendor, screen, temporary implementation, or stale migration path when the domain responsibility has a stable name.
- use a topic-only document filename that does not state its rule, action, or outcome.
- put two independently understandable routes, schemas, mappers, or workflows in one file merely because their names share a prefix.