Back to Blog
naming conventionspipelinedocumentationdata

Name It Once: A Naming Convention That Keeps Your Design Docs and Your Build in Sync

Four names for one sword is twenty minutes of grep per bug. A practical ID and asset naming convention for small teams: stable IDs vs display names, a grammar you can type from memory, and a rename procedure that does not break old saves.

GameDesignerX TeamNovember 7, 20267 min read

A tester sends you a clip: "the fire sword does nothing now." You open the project and find SwordFire_02. You open the balance sheet and find item_sword_fire. You open the design doc and find "Ember Brand." The localization file has WPN_FIREBLADE_NAME. Four names, one object, and twenty minutes of grep before you can even start looking at the bug.

Naming is the least glamorous thing in a design document and the one that quietly costs the most. Every cross-reference in your project — a loot table pointing at an item, a dialogue node pointing at a character — is a name someone typed from memory. Get the convention right in week two and it holds for the whole project. Get it wrong and you pay a tax on every lookup for three years.

Two names per thing, and only one of them is yours to change

The single most useful rule: separate the stable ID from the display name.

Role Example Who changes it When
Stable ID wpn_sword_ember Nobody, after creation Never
Display name "Ember Brand" Writers, marketing Any time
Loc key wpn_sword_ember.name Derived from the ID Never
Engine asset SM_wpn_sword_ember Artists, with the ID intact On re-export
File on disk wpn_sword_ember.json Nobody Never

The ID is a machine handle. It exists so that a loot table, a shop inventory, an achievement condition and a save file can all point at the same row without caring that your sword got renamed from "Flame Tongue" to "Ember Brand" to "Cinderbrand" over eighteen months. The display name is content: it should be free to change, and it should live in exactly one place, ideally the localization table, keyed off the ID.

The mistake almost everyone makes once is building the ID out of the display name and then renaming the display name. Now wpn_sword_flametongue shows up in your logs as "Cinderbrand", and every new hire loses an hour to that.

A grammar you can type from memory

A convention nobody can recall is a convention nobody follows. Use one pattern, three or four segments, lowercase snake_case:

<domain>_<subtype>_<subject>[_<variant>]
ID Reads as
wpn_sword_ember weapon, sword, Ember Brand
enm_grunt_spearman_elite enemy, grunt class, spearman, elite variant
lvl_mine_02 level, mine biome, second map
npc_elara character, Elara
qst_mine_rescue quest, in the mine, the rescue one
sfx_ui_confirm sound effect, UI, confirm
vo_elara_ch03_012 voice line, Elara, chapter 3, line 12

Rules that are worth being strict about:

  • Lowercase, underscores, ASCII only. No spaces, no hyphens, no accents, no apostrophes. These strings end up in file paths, URLs, shell commands and JSON keys. "Zoë's Café" is a display name, never an ID.
  • Zero-pad numbers, and pick the width once. lvl_mine_02 sorts correctly; lvl_mine_2 sorts after lvl_mine_11. Two digits caps you at 99 — fine for levels, not for voice lines. Use three for anything you expect to exceed 50 (dialogue lines, items, localization strings).
  • No status words. _final, _new, _v2, _temp, _fix, _real all become lies within a week. Status belongs in version control and your build-tracking notes, not in the name.
  • No initials, no dates. sword_mk_oct tells you who made it and loses you the ability to sort. Attribution is in the commit log.
  • Prefix by domain, not by project. Nobody needs mygame_ on every row.

Engine-side, keep the ID inside the asset name and let the engine's own prefix convention sit in front of it. Unreal projects commonly use type prefixes such as SM_ for static meshes, T_ for textures and M_ for materials; T_wpn_sword_ember_D is readable, sortable and still greppable by the design ID.

Keeping IDs stable when the design is not

Design changes. IDs should not:

Design change What happens to the ID
Renamed in fiction Nothing. Change the display string only.
Stats rebalanced Nothing. It is the same object.
Cut from the game Mark status: retired. Do not delete the row and never reuse the ID.
Split into two Keep the ID for the closer descendant, create one new ID.
Two merged into one Keep one ID, retire the other, record the mapping.
Changed category (item becomes a spell) Keep the ID even though the prefix is now wrong, or do a real migration. Do not half-rename.

Retired-not-deleted matters more than it sounds. Old save files, old playtest telemetry and old bug reports all reference IDs that no longer exist. If you keep retired rows with a status flag, a save from build 0.4.2 still loads and your telemetry from three months ago still joins against your item list. If you delete them, you get null-reference crashes in the oldest saves — exactly the ones from your most patient testers.

Two places where names become genuinely permanent: save-game keys and store-platform API names. Steamworks achievement and stat API names are effectively frozen once players have unlocked them — change one and you have orphaned people's progress. Decide those names in a dedicated pass before your first public build, not in a hurry during Steamworks prep, and write them down in the same table as everything else.

A rename is a migration, not an edit

When you genuinely must rename an ID — and it does happen, usually because the first one was wrong — treat it as a code change:

  1. Confirm nothing shipped with the old ID in a save file or platform API.
  2. Grep the entire project, including data tables, dialogue, loc files and scripts.
  3. Rename in the canonical source first (your design data), not in the engine.
  4. Add a mapping row: old_id → new_id, with the date.
  5. Update loc keys and ship a loc-key alias if translators are mid-pass.
  6. Run the game and load an old save.
  7. Note it in your decision log so nobody re-litigates it in four months.

If that feels heavy for one sword, that is the point: it is cheaper to argue about the name for ten minutes up front.

Catch drift with a ten-minute audit

Conventions rot silently. One person adds Enemy_Spider_Boss, nobody notices, and a month later there are nine of them. Make the audit mechanical and run it at every milestone:

  • Export the canonical ID list from your design data. If your docs live in GameDesignerX, the items, characters and locations modules hold the canonical rows, and the REST API or MCP server will dump them as JSON so you can diff them against the project without copying anything by hand.
  • Export the asset and data-table names from the engine.
  • Diff the two lists. Three buckets: in docs but not in build (unimplemented or retired), in build but not in docs (undocumented content), name mismatches (drift).
  • Regex the combined list against your pattern and print the violations.

A twenty-line script that prints "7 IDs do not match the pattern" is worth more than a style guide nobody reads.

The one-page convention doc

Keep it to a single page and lead with examples rather than rules — people copy the nearest example. It needs the pattern, the domain prefix list, the number-padding rule, the forbidden-words list, the ID-vs-display-name rule and the rename procedure. Put it where new contributors trip over it: next to the data schema, not buried in a wiki.

Checklist before you name 400 things

  • Domain prefix list agreed and written down (wpn, enm, lvl, npc, qst, sfx, vo, ach)
  • Number width chosen per domain (2 for levels, 3 for dialogue and items)
  • Display names live only in the localization table
  • Retired rows keep their IDs with a status flag
  • Achievement and save keys decided in a single pass before first public build
  • Rename procedure documented, with a mapping table
  • Audit script runs at every milestone

None of this makes your game better on its own. It makes every other improvement cheaper to find, cheaper to reference and cheaper to hand to someone else — which, on a three-person team shipping over two years, is roughly the same thing.