A close-up portrait of a man with a salt-and-pepper beard wearing a white collared shirt against a textured beige background.
A close-up portrait of a man with a salt-and-pepper beard wearing a white collared shirt against a textured beige background.

Names steer the code

Jesse James Richard
|
Feb 17, 2026
|
9 min read
#Architecture
#AI & Agents

I keep a thesaurus open when I name things, and not to find prettier words. Finding the name is how I work out whether I understand the thing I am about to build.

Before a line of a new package in Giant Context exists, its name forces the questions that matter. What job does this do? What belongs inside it? What must never bleed into it? If the name will not land, the thinking is not finished, and building anyway means building without the answers.

A name is a boundary

Hunting for the exact word is drawing a boundary. Calling a package media and calling it files are not two labels for one idea. Media says images, video, audio, the things a site displays. Files says every document a customer hands over, which includes all the media and also the PDF of last year's pricing that nobody will ever display and everything will need to read.

One word is a subset of the other, and choosing between them decides what the package is allowed to carry for as long as it exists. That decision is available before the first commit and it costs an afternoon with a thesaurus.

Code grows in the shape of its name

A name is not a label on a package. It is a standing instruction to everyone who adds to it afterwards.

Every later decision about where code belongs gets judged against the word. Does this go in ui? Is this core's job? Name a package well and those questions answer themselves for months. Name it badly and every one of those small answers lands slightly off, because people build toward what the word says rather than toward what you meant. The steering starts the minute the name exists.

That extends past packages, down to the file. A component here is a directory, and the directory carries the name while the files carry roles:

One component, three files

Button/├── index.ts     export { Button } from "./view"├── view.tsx     presentation only└── types.ts     ButtonPropsType

Never Button/Button.tsx. The path says the name once and then says what each file is for, so nothing is read twice to find out what it is. The same shape holds everywhere: a block is Blocks/Divider with an index, a view and a settings file, and knowing one directory means knowing all of them.

Some names are consumed rather than read. An API route declares an operationId, and that string is not documentation. The generators use it as a name. createOrganization becomes the generated zod schema, the client method, the hook useCreateOrganization, and the cache key those hooks invalidate against. Four artifacts, one string, spelled once in the route file.

Rename the operation and all four change on the next pass, which is the useful half. The other half is that a vague operationId produces four vague names in code nobody wrote and nobody will think to fix, because a generated file is not where anyone looks for a naming problem.

The second reader

All of that has always mattered for the next engineer. What changed is who else is reading.

Most of the code in this repository is written by AI tooling that reads the code already there, and it takes names literally, because literal is all it has. A person who hits a badly named package infers what you meant, mutters, and carries on. A model does not infer. It builds toward what the word says, at generation speed, so a name that lies produces a hundred files agreeing with the lie before anyone reads one of them.

I have no example of that happening here, and the reason is that the names were settled before there was anything to generate against. So this is a prediction rather than a finding. I believe a wrong name costs more than it did three years ago, and I have arranged the codebase as though that is true, and I have not paid the bill and cannot tell you its size.

Casing is not taste

The mechanical half is written down, and the agents and I both follow it.

The naming conventions, from the standards doc

| Element          | Convention                 | Examples                 || ---------------- | -------------------------- | ------------------------ || React Components | PascalCase                 | Button, UserProfile      || Functions        | camelCase, max 3 words     | getData, handleClick     || Variables        | camelCase, max 3 words     | userId, isActive         || Constants        | camelCase                  | defaultValue, maxRetries || Types            | PascalCase + "Type" suffix | UserType, ConfigType     || Interfaces       | PascalCase + "Props"       | ButtonProps              |
STRICTLY FORBIDDEN:- SCREAMING_SNAKE_CASE - Never use this anywhere- Long names - Maximum 3 words, aim for 2- Abbreviated names that aren't obvious - Use `user` not `usr`

The three-word cap is the thesaurus rule wearing work clothes. A two-word name has no room to hedge, so it forces you to find the word that actually fits rather than stacking qualifiers until the name approximately describes something.

I came up through LAMP and preferred snake_case for years. It took about five years to stop resenting camelCase, and the resentment was not unreasonable, because the research is genuinely split. Binkley and colleagues tested identifier styles with timed responses in 2009 and found camelCase produced higher accuracy, with subjects trained in camelCase reading it fastest. Sharif and Maletic replicated that study with an eye tracker the following year and found no accuracy difference at all, but faster recognition for the underscore style, noting that their own subjects had been trained mostly in underscore. Beginners in that study benefited about twice as much from underscores as experienced programmers did.

Two studies, opposite results, and both note the same confound. People read fastest in the style that taught them. That is a finding about training rather than about typography, and it means the argument I was having with camelCase was an argument with my own history.

The split was never about legibility anyway. Early languages were case-insensitive or uppercase-only, so an underscore was the only separator available, and that lineage runs from C through Python and Ruby into SQL. Case-sensitive languages arriving later could separate words with a capital instead, and camelCase travelled from Smalltalk to Java to JavaScript. Preferring one is mostly a fact about which ecosystem you learned in.

So I relented. TypeScript is camelCase because JavaScript is camelCase, and fighting an ecosystem's convention costs more than the convention does.

The seam

Two things in this codebase are not camelCase, and both are right. Python is snake_case because PEP 8 says so. Postgres columns are snake_case because that is what SQL has always been. Neither is a place to express a preference, and a codebase with a firebaseUid column is announcing that nobody there has used a database.

Which means every read crosses a casing boundary, and something has to do the conversion. Here it is done by hand, in the SQL, every time:

Every read, aliased by hand

SELECT  id,  email,  firebase_uid   AS "firebaseUid",  is_active      AS "isActive",  created_at     AS "createdAt"FROM usersWHERE id = $1

Writes go the other way, with snake_case column lists taking camelCase values positionally. Single-word lowercase columns, id and name and slug and email, need no alias and get none.

There is no automatic conversion. The Postgres driver is a plain pool with no row mapper, there is no camelize helper, and there is no ORM. The names on an object are exactly the names the SQL declared, so a forgotten alias does not throw. It produces one created_at key in the middle of an otherwise camelCase object, and you find it when something downstream reads undefined.

A driver-level mapper would remove that whole class of mistake, and I have not built one. The reason is blast radius rather than principle. Every query in the codebase currently declares its own output names, so a mapper changes all of them at once, including the ones I have not read in weeks. The shared query helpers already auto-convert in the one direction where the input arrives as a string from outside, so a sort parameter of createdAt becomes ORDER BY created_at, and that was safe because it touched one code path. Doing it to every projection is a different size of change, and I am not confident it is the wrong idea.

What matters more than automating it is that the seam has one location. Cross-casing inside a single layer is what makes a codebase unreadable: user_id beside userId in one function, both valid, neither wrong, and now every name has to be checked rather than read. One boundary, at the query, is a rule you can hold in your head. Casing that varies by whoever wrote the line is not a convention at all.

Rename the day it stops being true

A name that was right can stop being right, which is a different problem from getting it wrong.

Media was accurate for as long as the package held images and video. Then the AI started reading customer documents for grounding, and the package began carrying PDFs and spreadsheets. The word had not become inaccurate through carelessness. The contents had grown past it, and a name that describes less than the thing it names will quietly keep new work out of the package where it belongs.

The check is one question, asked whenever a package's job shifts. Does the name still describe the role? The day the answer is no, everything built under it afterwards aims at a concept that no longer exists. Rename that day.

Here that is cheap, one repository to sweep and most references generated from schemas, so a stale name rarely survives the week. If renames are expensive where you work, that is not permission to skip the maintenance. It is a bigger argument for the day-one sweat, because you will live with your first guess for years.

Manage names like you manage dependencies

Naming is day-one, coding-101 material, and it is still the decision I spend longest on, because the audience for a good name doubled and the new half never skims.

References

Binkley, D., Davis, M., Lawrie, D., and Morrell, C., "To CamelCase or Under_score", Proceedings of the 17th IEEE International Conference on Program Comprehension (ICPC'09), 2009.

Sharif, B. and Maletic, J. I., "An Eye Tracking Study on camelCase and under_score Identifier Styles", Proceedings of the 18th IEEE International Conference on Program Comprehension (ICPC'10), Braga, Portugal, 2010, pp. 196 to 205.

The localization I skipped

#Data
#AI & Agents
#Testing

I skipped localization to prove the product first, and I cannot tell you what the retrofit cost because it never had a boundary. Here is how to do it ...

Jesse James Richard

|

Feb 12, 2026
Read previous

Building something like this

I'm Jesse. I build platforms end to end, and I'm open to work. If this is the kind of engineering you need, get in touch.

Contact Jesse
Home
About
Contact
Sitemap
Privacy Policy
Terms of Service
Cookie Policy