Overview
A filename is a tiny piece of API. It is the thing people type, grep, and link to. The rules below pick one casing per ecosystem, ban domain-noun abbreviations, anchor dates in ISO, and resolve filename collisions. They follow the same logic as general-principles: a good name kills its own comment.
kebab-case for files unless the language says otherwise
Use lowercase, hyphen-separated filenames by default. They survive case-insensitive filesystems, URL slugs, and shell tab completion without quoting.
- Markdown, HTML, config:
naming-conventions.md,index.html,pre-commit-config.yaml. - Static assets:
hero-image.png,favicon-32.png. - Top-level project files keep their canonical names:
README.md,LICENSE,Dockerfile.
The exceptions are languages whose tooling expects a specific case. Honor the toolchain; do not fight it.
- Python:
snake_case.py. Module names map to import paths; hyphens are illegal in identifiers. - JavaScript / TypeScript files:
camelCase.tsfor modules,PascalCase.tsxfor React components. Some teams usekebab-case.tsfor non-component files; pick one and document it. - Rust:
snake_case.rs. Crate names use kebab-case inCargo.toml, snake_case as the import. - Go:
lowercase.goorsnake_case_test.go. No camelCase, no hyphens. - Swift:
PascalCase.swiftfor type files (UserProfile.swift).
Name describes intent, not implementation
The filename should tell the reader what role the file plays, not how it does it.
invoice-parser.ts, notxml-utils.ts. Two months from now the implementation might be JSON; the role does not change.auth-middleware.py, notdecorator.py. “Decorator” describes how, not what.daily-report.sql, notquery-3.sql. The role survives a rewrite.
A filename that names the implementation is a refactor away from being a lie. Name the responsibility.
Never abbreviate domain nouns
The five-character savings cost a lookup every time a new reader hits the file. Spell it out.
customer.ts, notcust.ts.subscription-renewal.py, notsub-ren.py.pull-request-review.md, notpr-rvw.md.
The exceptions are abbreviations the field already settled on:
id,url,uri,http,db,api,dto,json,xml,csv.
If a reader has to ask “what does mgr stand for?” once, the name failed. Match the spelling to the domain language used in tickets and docs.
ISO dates in filenames
Dates in filenames are sortable when they use ISO 8601: YYYY-MM-DD.
posts/2026-05-14-quartz-release-notes.md
migrations/2026-03-01-add-user-index.sql
backups/db-2026-05-14T03-00-00Z.sql.gz
Avoid 05-14-2026, 14-may-2026, or today.sql. None of them sort. ISO times use T and either - or : (filesystems on Windows ban :); pick - and keep the format consistent. See folder-hierarchy for date-partitioned directories.
Resolve filename collisions with paths, not numbers
Two files named utils.ts in different folders are fine until someone greps. Add a path-distinct prefix or rename to something specific.
- Prefer renaming:
string-utils.tsanddate-utils.tsinstead of twoutils.ts. - When a rename is impossible (framework convention), keep the names and rely on the folder path for disambiguation:
pages/blog/index.tsxandpages/about/index.tsx. - Never resolve a collision with a numeric suffix (
utils-2.ts). The number tells the reader nothing about which utils it holds.
In Obsidian and Quartz, slug collisions force folder-prefixed wikilinks ([[seo/technical]] vs [[frontend/technical]]). See obsidian.
Plural vs singular: pick by what the file holds
A file holds one thing or many. Match the noun number.
user.tsexports aUsertype;users.tsholds a collection or a CRUD module for users.route.tsdefines one route;routes.tsregisters many.- React component files are singular by convention:
UserCard.tsx, notUserCards.tsx, even if the component renders a list.
Folders follow the same rule: migrations/ (a folder of many migrations), config/ (a folder of one config split into pieces).
Test files mirror the source name
A test file’s name should make the source file obvious.
- Python:
test_invoice.pyforinvoice.py. Pytest discovers it automatically. - JS/TS:
invoice.test.tsorinvoice.spec.tsnext toinvoice.ts. Pick one suffix and stick with it. - Go:
invoice_test.gonext toinvoice.go. The_test.gosuffix is mandatory; Go’s toolchain uses it for collection. - Swift:
InvoiceTests.swiftforInvoice.swift.
A test file that does not mirror its source name will eventually drift away from it. See testing.