Skip to content

Your AI is bad at naming things ​

A database schema diff replacing vague AI field names with clear ones, and a PR heading asking where what belongs

AI is bad at writing. And I don't mean articles. I'm not saying it should write books for you, or replace writing at all. I'm talking about using it to help document systems, like database schemas and PRs, where what it writes is so vague it's hard to read. It doesn't write like people think.

When I had it design database tables and fields, it did things like name a field "where it goes". Or a field called "why", or "what went wrong". None of this makes sense. A real person would label it "failed reason" or "error message". And when I use it to create prototype UIs, it names things just as terribly. It also makes lazy developers easy to spot, because they let AI choose how to name things.

Then there are PRs. I can't stand reading them when the headlines are "Who", "What went wrong", and "How it failed". I've seen one called "Try by hand". What does that even mean? Another one was "Where it belongs". Where what belongs? To a person, it makes no sense.

A heading should be labeled for the purpose of the reader, so they understand what they're looking at. And more importantly, it should be standard, because AI doesn't understand standards. It's an LLM, and an LLM is basically randomized tokens of output. You never get consistency.

So I've had to create skills around this. One is what I call managing-github. It gives most of the AI I use a standard issues template when the repo doesn't already have one, and it has helped significantly.

It does a few other things too: how to write release notes, how to version (Semantic Versioning, when the repo doesn't already have its own scheme), and standards for commits and branches. It's a core skill that all AI should have when doing development.

The other one I created is specifically for grammar. It's called naming-grammar-conventions, and when I have it help me sketch up ideas and prototypes, or write documentation and PRDs, it can do it somewhat better. The core rule is one line: name each value or entity for what it is, and each operation for its outcome. So "Why" becomes "Reason". "How retries are handled" becomes "Retry limit".

It also keeps one term per concept in a lexicon that lives in the repo, and the AI has to look a term up before it invents one. Otherwise the database says cust_flg, the API says customer_type, the screen says Client, and every new agent session invents another. The skills still aren't perfect, but they do help.

Here's the whole naming skill so you can see what I mean. You can also download the SKILL.md and drop it into your own setup. I'll write about how I write and test skills in another post.

md
---
name: naming-grammar-conventions
description: Use when choosing, reviewing, or standardizing software terminology across source code, schemas, APIs, logs, and product interfaces, especially when one concept crosses several layers. It supplies a lexicon-led grammar for precise terms, layer-appropriate names, clear interface text, and compatibility-safe renames. Do not use for general prose editing, brand naming, marketing copy, or work where naming is incidental.
---

# Naming and grammar conventions

Keep one vocabulary per concept across code, data contracts, and product interfaces. Preserve
intentional layer forms and compatibility mappings instead of forcing identical spelling.

## Name the meaning

> **Name each value or entity for what it is, and each operation for its outcome.**

A value or entity name states what it represents. An operation name states the outcome it produces. A description explains mechanism, context, or constraints. These are different jobs; mixing them produces names that are hard to scan and easy to misread.

| Layer | Mechanism (wrong) | Meaning (right) |
|---|---|---|
| Column header | `How retries are handled` | `Retry limit` |
| Field label | `Why is this archived?` | `Reason` |
| DB column | `retry_mode_flag_2` | `retry_limit` |
| Function | `processData()` | `normalizeAddress()` |
| Class | `InvoiceHelper` | `InvoiceRateSheet` |
| API error | `ERR_5` | `INVOICE_NUMBER_TAKEN` |
| Event | `invoiceUpdateHappened` | `invoice.archived` |

## The four failure modes

Assume you are about to commit one of these and check explicitly.

**1. Naming the mechanism instead of the value.**
`How retries are handled` → `Retry limit`. `handleUserStuff()` → `deactivateUser()`. The name describes the process rather than identifying the thing.

**2. Question words and sentences where value names or compact labels belong.**
`Why` → `Reason`. `Who asked` → `Requester`. `did_the_user_confirm` → `is_confirmed`. Name values and entities with noun phrases or established state terms; name actions with verbs. A form journey may deliberately use a direct question when that pattern helps the user.

**3. Blending two jobs into one string.**
A label trying to also be an explanation. A function name trying to describe its whole algorithm. A column name carrying its own documentation. Split it: the noun stays, the explanation moves to a tooltip, a docstring, or a comment.

**4. Prose register where a factual statement belongs.**
`There is nothing here right now` → `No archived documents.` `// this is where we cleverly loop back around and fix the thing` → say why, in one line, or delete it.

Generated names and interface text often blend prose, mechanism, and explanation. Check these failure modes explicitly instead of assuming a fluent sentence occupies the correct slot.

## Test each proposed name

Run these on any name or string before writing it.

1. **Meaning test.** What fact, value, action, or outcome is established? Do not choose an exact
   name, migration, endpoint, event, interaction, owner, or risk severity that the evidence does
   not support. If the outcome is unknown, say that the exact name is unresolved; do not fill the
   gap with illustrative domain names that look like recommendations.
2. **Canonical-term test.** Does the concept already have a term in the lexicon? Reuse it instead of coining a synonym.
3. **Slot test.** Is this explaining something? Then it belongs in a description slot, not a name.
4. **Constraint test.** Is a spelling fixed by a published or third-party contract, regulated text,
   localization, privacy, or an established platform convention? Preserve it and document the
   mapping.
5. **Fortieth-time test.** Read it as the person who encounters it for the fortieth time: the operator scanning the table, the engineer reading the stack trace at 2 AM. Does it help them scan, or make them read?

## Keep one canonical term

Give each concept one canonical term. Canonical parity means shared meaning, not character-for-character
spelling. Express the term through each layer's convention and document intentional compatibility,
vendor, localization, privacy, or audience-specific mappings:

```
consumer          DB column / table
consumer_id       foreign key
consumer          API field
Consumer          class / type
consumer          variable
Consumer          UI label
Consumers         page title, nav item, export header
```

When the database says `cust_flg`, the model says `isCustomer`, the API says `customer_type`, and the screen says `Client`, you have four vocabularies for one idea. Every engineer, every support agent, and every user pays a translation tax forever, and every new agent session invents a fifth.

**The parity test:** when two layers represent the same concept, confirm that their names map to one canonical term. Do not require mechanical spelling parity: database columns, public API fields, localized labels, and compatibility aliases may differ deliberately. Record the mapping.

## Look up terms before inventing them

Agents **look terms up before proposing them.** Consistency is unreachable through better writing
alone, because every file and screen written independently can invent a fresh synonym for the same
concept. When no approved term exists, state the missing evidence, propose only the smallest
supported candidate, and request the owner's decision instead of filling every layer speculatively.

A product with recurring domain terms keeps a **lexicon** in the repository: one canonical term per concept, its layer forms, its definition, and the synonyms to avoid. The lexicon is the artifact that maintains the product vocabulary.

If the lexicon does not cover a recurring concept, propose an entry before inventing another term. Add or rename product vocabulary only with the owner's authority.

## Apply the product defaults

Use these as defaults for operator-style software. Record product-specific exceptions in the lexicon; regulated wording, published contracts, platform conventions, localization, and established domain language take precedence.

| Don't | Do | Why |
|---|---|---|
| Em dashes used as routine asides | Period, parentheses, or restructure | Keeps operator text direct. A product may choose another punctuation style; `—` may also be its null marker |
| Abbreviations that are not domain standard (`cust`, `qty_rcv`, `mgr`) | Full words | Saves keystrokes once, costs comprehension forever |
| Negated names that merely invert a positive condition (`is_not_active`, `disable_notifications`, `hideCompleted`) | Positive form (`is_active`, `notifications_enabled`, `show_completed`) | Double negatives at every call site and an ambiguous "off" state; preserve a negative form when the domain fact or fixed contract is genuinely negative |
| Type or class in the name (`strName`, `reason_text`, `InvoiceObject`, `user_list`) | The meaning (`name`, `reason`, `Invoice`, `users`) | The type is already declared, and it changes |
| Grab-bag words (`data`, `info`, `item`, `thing`, `util`, `helper`, `manager`, `handler`, `stuff`, `temp`) | The specific thing | These are names that mean "I have not decided what this is" |
| Numeric suffixes (`data2`, `processUser3`, `column_b`) | Distinct meanings, distinct names | The suffix is the missing distinction |
| Vague pronouns for named entities (`Something here already exists`) | Name the entity | The system knows what it is; say it |
| Unchosen first-person voice (`we couldn't find`, `our records`) | The product's defined voice or a factual statement | Prevents one screen from inventing a different speaker |
| Marketing adjectives in functional text (powerful, seamless, robust, smart) | Nothing | Says nothing to someone doing the task |
| Unchosen exclamation marks and emoji in system text | The product's defined feedback style | Prevents accidental tone changes in operational text |
| Minimizers (simply, just, easily, only) | Omit | They add no recovery information and can understate difficulty |
| Hedging (`it looks like`, `it seems`, `apparently`) | State what is known | Erodes trust in every other message |
| Inconsistent verbs for one operation (`get` / `fetch` / `retrieve` for the same act) | One verb per meaning, product-wide | Every synonym implies a distinction that does not exist |
| Stale names after a refactor | Classify the boundary, then rename privately or stage a compatible migration | A wrong name misleads, while an unsafe rename breaks consumers |

## Choose the product register

One decision per product, recorded at the top of the lexicon file.

| Register | Fits | Character |
|---|---|---|
| **Operator** (default) | Internal tools, admin panels, back office, developer tools | Dense, factual, terse; optimized for scanning |
| **Practitioner** | Professional SaaS with self-serve onboarding | Same grammar, more explanation slots filled |
| **Consumer** | Public apps, first-run flows | Same grammar, plainer vocabulary, more guidance |

Register changes **how much explanation appears** and **how domain-specific the vocabulary is**.
Keep each slot's purpose stable while allowing language, localization, accessibility, and established
design-system grammar to shape the surface form. "Plain and human" is not the same as "chatty and vague."

For new work with no product evidence, operator register is a fallback. In an existing product,
inspect its interface and conventions before applying that fallback; keep recommendations
conditional when the evidence is unavailable.

## Apply the conventions

Before writing any name or user-facing string:

1. **Identify what you are naming**: a stored value, a behavior, a type, an event, or a slot on screen. If you cannot say which, you do not yet know what you are writing.
2. **Look up every recurring concept in the lexicon.** Missing? Propose an entry before coining a synonym.
3. **Apply the form** for that kind of name: noun phrases or established state terms for values and entities, verbs for actions.
4. **Split, don't blend.** Name in the name; explanation in the docstring, comment, tooltip, or help text.
5. **Check parity** across layers. Distinguish accidental drift from an intentional, documented mapping.
6. **Scan the product defaults.**
7. **Apply the fortieth-time test.**
8. **Report proportionately.** Answer the requested naming scope first. Separate facts, assumptions,
   owner decisions, and compatibility risks. Do not invent adjacent architecture or repeat the same
   rationale for every layer. Omit executive summaries, status sections, checklists, and exhaustive
   layer tables unless the user asks for them or they materially clarify the decision.

## Resolve the convention stack

Consistency means each owner supplies one part of the name; it does not mean copying one layer's
spelling into every other layer.

1. The product lexicon supplies the canonical concept and meaningful distinctions.
2. The data model supplies engine-independent value composition, cardinality, relationships, and
   model shape.
3. The active framework or ORM supplies derived forms such as model, table, key, relationship, and
   timestamp conventions.
4. The database engine supplies engine-specific identifier and migration constraints.
5. Published, generated, vendor, regulated, and localized forms remain documented boundary mappings.

Inspect the owning stack before recommending a spelling. Preserve one convention within each
bounded layer, configure deliberate exceptions explicitly, and do not export Laravel, PostgreSQL,
MySQL, SQLite, or another ecosystem's convention as a universal rule.

## Respect established constraints

State the exception in the lexicon file rather than deciding case by case.

- **Regulated wording wins.** Legal disclosures, consent language, and required notices are written by counsel and reproduced exactly.
- **Established platform and language conventions win.** Follow the idioms of the language and framework in use over the preferences here. Ruby is not Java is not Go.
- **Real domain jargon wins over plain language.** If practitioners say "endorsement," the term is `endorsement`, not `policy change`. Plain language means avoiding *invented* abstraction, not avoiding the reader's actual vocabulary.
- **Existing codebase conventions win over local preference.** Consistency with the surrounding code beats correctness in isolation. If a codebase is uniformly wrong, propose a migration rather than introducing a second convention.
- **Fixed third-party names stay at the boundary.** Preserve vendor fields and generated contract
  names verbatim, map them to the canonical term at the adapter, and do not spread the external form
  into new first-party names.
- **Distinct meanings stay distinct.** Similar words may be valid terms for different concepts;
  never put a contextual term on a global avoid list or collapse states merely to make names match.
- **Conflicting constraints require an owner decision.** Do not manufacture a universal precedence
  rule when regulation, accessibility, privacy, localization, and a published contract pull in
  different directions.
- **Marketing and landing pages are out of scope.** Different discipline. Do not let that voice migrate into the product.

I honestly hate that we're all being pushed to use AI. But it's unavoidable, and now it seems like everyone is using it to offshore all of their writing: docs, PRDs, even emails. So use it where it helps speed up the tedious processes.

I need to be clear about this part. It should not be used as a way to replace writing entirely, or to replace work entirely. If you need to use it (and most people must now, because everyone feels like they have to just to keep up with everyone else), be an operator. Don't let it run rampant. That's how you get AI slop.


How are you getting AI to name things like a person would? Tell me on Bluesky or GitHub.