Jan 02, 2025·7 min read

Domain language versioning before your API changes

Learn how domain language versioning keeps business terms stable in your code, so partner, product, and API changes create less churn.

Domain language versioning before your API changes

Why external changes keep breaking internal code

Most products describe the same business idea in different ways. Sales says "customer." Billing says "account." A partner calls the same thing a "subscriber." When developers copy those outside labels into the code, the code starts to mirror every conversation instead of the product itself.

That feels harmless at first. A class gets named PartnerAccount because the first integration used that term. A few months later, marketing changes the site copy to "Organization," and a new partner sends docs that say "Workspace." The business barely changed, but now three names point to one idea. People waste time asking whether those names mean different things.

Outside language changes faster than internal logic. Partner docs get revised. Product copy gets rewritten. Sales teams rename plan tiers to make them easier to sell. The code should not absorb every one of those shifts, but teams often wire those labels straight into models, endpoints, enums, and database fields.

Once that happens, every rename spreads farther than expected. It leaks into test names, admin screens, analytics events, support macros, and internal docs. Even when the behavior stays the same, people still touch dozens of files just to keep names aligned. That work is dull, and it still creates bugs. One missed rename in a report or permission check can confuse support or break a test.

The deeper problem is not the rename. The codebase has no protected inner vocabulary. When outside terms become inside terms, the system loses a stable center. That is why domain language versioning often matters before API versioning. If the code keeps one durable name for each business concept, teams can map changing partner words and product labels at the edges, where they belong.

What should stay stable inside the codebase

The names that describe your business should move the least. If your company sells subscriptions, keep words like "subscription," "renewal," and "cancellation" steady in the code. A vendor may call the same thing a "plan," a "price," or a "contract." Your code should not chase those labels.

This matters because outside terms change for reasons that have nothing to do with your product. A payment provider renames a field. A partner changes webhook names. A sales team updates site copy. If your internal models mirror those words, one small change spreads into tests, reports, admin tools, and support notes.

Some teams call this ubiquitous language. The label matters less than the rule: one term should carry one meaning everywhere. If "customer" means a paying company, do not also use it for an end user, an account owner, and a trial signup. Pick separate words and keep them separate.

Short definitions help more than most teams expect. When a word causes even one argument, write a plain definition in the repo. Keep it brief. "Account: the company record that pays us." "User: a person who signs in under an account." A tiny glossary like that cuts confusion before it reaches the code.

Keep internal terms separate from request and response labels. Treat API fields as translation, not truth. Your edge layer can map a vendor field like current_price_id to your own term, such as "active subscription plan," and the rest of the app can stay untouched.

A simple test works well: if you switch vendors next month, which names should stay? Those are the names that belong in your domain models, service methods, events, and tests. Everything else should live at the edges, where change is cheaper.

Version the language before you version the API

Business terms are product decisions. If you treat them like filler labels, your code starts copying every naming change from sales, support, or a partner API. Then a small external edit feels bigger than it is.

Version the words first. Version the API only when the meaning really changed. That is the point of domain language versioning. A renamed field should not force a new internal model if the business idea stayed the same.

Say your product used the term "customer" for years. Then billing starts calling the same person an "account owner." If both words still mean the same role, keep one internal term in the codebase and map the new external label to it. That avoids a chain reaction across services, tests, docs, and dashboards.

Add a new term only when the meaning changes in a real way. You can usually see that in concrete places: the new thing has different rules, different people can hold it, reports count it differently, or pricing and permissions depend on it. If none of that changed, you probably have a rename, not a new concept.

Old names should keep working for a while during a transition. That does not mean you keep them forever. It means you give the team time to migrate without breaking live work. In practice, teams often keep the old name as an alias in adapters, request mappers, or serializers while the internal model stays stable.

Retirement needs a date, not a vague plan. Mark the old term in code and docs. Write the replacement next to it, add the removal date, and make one person own the cleanup. If you skip that step, old and new names will sit side by side for years.

Careful teams already work this way, whether they use the phrase "domain language versioning" or not. They protect business meaning first and change the edges second. That order saves time, lowers risk, and keeps the code readable months later.

A simple method that works

You do not need a big process. Start with the words your team already uses in tickets, demos, support chats, and sales calls. The goal is not to make the language sound clever. The goal is to pick names that still make sense six months from now, even if partners, vendors, or API fields change.

A small team can usually do this in one working session, then keep it alive during normal product work.

  1. Write down the business nouns and actions that show up every day. Focus on product terms, not database names. If your product handles orders, refunds, quotes, approvals, and renewals, start there.
  2. Find duplicates and pick one internal name for each idea. Teams often use two or three words for the same thing, such as customer, client, and account. Choose one term for the codebase, and keep the others as aliases only in notes or adapters.
  3. Map every outside name to the internal term. A payment provider might send payer_id while your team says customerId. Keep the external field at the edge, then translate it once before the rest of the code touches it.
  4. Add a short note when meaning changes. Keep it plain: what changed, when it changed, and which old name or rule still exists for older data.
  5. Review naming changes before you publish API updates. If a new endpoint forces your team to rename an internal concept, stop and ask why.

A simple example makes the point. If one partner says subscriber, another says member, and your product team means paying user, pick one internal term such as subscriber. Then translate the outside labels at the boundary. One decision like that can save hours of cleanup later.

A small example from a booking product

Untangle Product Language
Turn mixed terms across product, support, and code into one clear internal vocabulary.

A team working on a booking app had one problem that kept spreading through the code. The same person showed up as a "guest" on one screen, a "traveler" in booking data, and a "customer" in payment and support tools.

That looked harmless at first. Then bugs started to pile up. A developer would update the "guest" rules and forget that the payment flow used "customer." A support script would count "travelers" and miss people who paid but had not finished the trip form.

The mess got worse when the team added a second payment partner. One partner sent payer_id. The other sent user_id. Both fields meant the same thing: the person who paid.

Instead of passing those partner names through the whole app, the team picked one internal term and stuck to it. Inside the core code, that person became paying_customer.

At the system boundary, payer_id and user_id both became paying_customer.id. The app kept "guest" and "traveler" only when they meant the actual trip participant. That one decision removed a lot of noise. Core code no longer cared which payment partner sent the event. It only cared whether a paying_customer existed and what booking that payment belonged to.

The quiet win came later. When one partner renamed a field during an API update, the team changed the adapter and left the rest of the system alone. Reports still made sense. Support scripts still used the same internal name. New developers could read the code without guessing whether "customer," "guest," and "traveler" were the same person.

Teams often rush to version API endpoints. Often, the better first move is to version the words inside the code. If those words stay stable, external changes hurt a lot less.

Where teams usually go wrong

Teams get into trouble when they let each new integration rename their internal world. A partner calls a customer an "account," another calls it an "organization," and soon the code follows both. After a few rounds, one business concept has three names, and every API update spills into models, tests, reports, and support notes.

Another problem is even more common: one word means different things to different people. Product says "order" and means the thing a buyer placed. Engineering says "order" and means the sequence of steps in a workflow. Support says "order" and means the invoice record they can search. The team thinks they agree because they use the same word. They do not.

That confusion spreads fast. Someone changes a label in the app and updates a few classes, but the docs still use the old term. Support macros keep the older wording. Analytics dashboards keep a third name because nobody wants to break reports. Then a bug report arrives, and three teams spend half an hour figuring out whether they are even talking about the same thing.

Teams also change names too abruptly. They remove the old term on Monday, merge the new term on Tuesday, and expect everyone to adjust at once. That usually creates more mess than the original rename. A short overlap works better. Keep the old and new names side by side for a while, mark one as on the way out, and give people time to update code, docs, and habits.

The worst cases have no shared source of truth at all. Naming rules live in chat, ticket comments, and people's memory. A startup might decide that "workspace" now means "client account," but if that decision stays in Slack, half the repo will still say "workspace" next month. New hires learn the wrong term from old files and copy it again.

You can usually spot naming drift early. Every new integration triggers internal renames. Product, engineering, and support define the same noun in different ways. Old and new labels appear together with no clear rule. People settle naming debates by searching chat history instead of reading one shared definition.

Most teams do not need a bigger process. They need one small glossary, one clear meaning per term, and a short overlap when names change.

Quick checks before you ship

Map Vendor Terms Once
Design adapters that translate outside labels without touching your core models.

A release is a bad time to discover that two people use the same word to mean different things. A 10-minute review can catch that.

Pick the newest teammate in the room and ask them to explain each main business term in plain words. If they hesitate, or if two people give different answers, the term is still fuzzy. That fuzziness will leak into handlers, tables, events, and docs.

Keep the pre-ship review simple:

  • Does each main term map to one business meaning only?
  • Did vendor names stay at the system boundary instead of spreading into domain code?
  • Did someone record what changed, when it changed, and what old term still exists during the overlap?
  • If two names need to live side by side for a release or two, do reads, writes, logs, and analytics still work?

A small example shows why this matters. A billing provider renames trial_end to grace_period_end. Your product team still talks about a trial. Good code keeps "trial" inside the app and translates the provider field in one adapter. Bad code spreads the new provider name across services, jobs, and dashboards.

Small teams often skip this because they think everyone already knows the language. That is exactly when naming drift slips in. If one person can explain the term, one file records the change, and one adapter handles outside naming, the rollout is usually safe.

How stable language helps the whole team

Reduce Rename Churn
Fix the source of repeat renames instead of patching the same confusion each sprint.

When teams keep the same business terms inside the codebase, daily work gets quieter. Fewer meetings turn into naming debates. Fewer tickets bounce between support, product, and engineering because each group means something different by the same word.

Docs get easier to trust too. If the code says "customer," the API docs say "customer," and support replies use "customer," people learn the system faster. They stop wasting time asking whether "account," "organization," and "workspace" are three different things or one thing with three labels.

Small teams feel this quickly. Support writes clearer replies because the product language matches the code. Sales stops promising one label while the product shows another. Engineers estimate work with less guesswork because names do not shift halfway through a sprint. Refactors stay smaller because teams change adapters, payloads, and UI text before they touch the core model.

Planning improves too. If a feature starts as "team billing" and later becomes "organization billing," estimates often break for a simple reason: nobody agrees on the actual object anymore. One developer thinks it belongs to a user group, another thinks it belongs to a legal entity, and support thinks it maps to a paid plan. The code follows that confusion.

A stable internal language cuts that mess early. You can rename fields at the edge, map old terms to new ones, and keep the business model steady inside the app.

This matters most in small product teams where one person covers product, support, and delivery in the same day. If the team picks one term and protects it, handoffs get simpler. The backlog reads better. API docs stop drifting away from the product. When change does come, it lands on the edges first instead of cracking through the whole codebase.

Next steps for a small team

You do not need a rewrite to make this work. Pick one flow that already causes confusion, then clean up the language around it. Signup, billing, and orders are good places to start because small naming mistakes there spread fast into support, product docs, and API work.

Use a practical rule: fix the terms that cost you time every week. If product says "customer," support says "account," and the code says "user," people waste energy translating instead of building. Choose one internal term, keep it steady, and map outside labels at the boundary.

A good first pass is simple. Pick one messy flow, list the words used in product, code, API, and database, then choose one internal name for each business concept. Rename the worst conflicts first and write the mapping in a short team note. That is enough to start. The point is not perfect naming on day one. The point is to stop fresh features from making the mess bigger.

Small teams get the most benefit when naming review becomes part of planning instead of cleanup nobody schedules. When product and engineering discuss a feature, spend a few minutes on three questions: what is this thing called in the business, what is it called in code, and what will outside systems see? That short check can prevent weeks of drift.

Billing often shows the problem clearly. A team might use "plan," "subscription," and "contract" for the same idea depending on who wrote the feature. Then an API change lands, and everyone argues about meaning before anyone touches code. Stable internal names make those changes much cheaper.

If the mapping still feels tangled after one pass, a short review with Oleg Sotnikov at oleg.is can help sort out domain language, API boundaries, and the broader CTO decisions around them. That kind of outside review is often faster than another month of patching names one by one.

Start small, keep the internal words steady, and protect them every time the product changes.

Frequently Asked Questions

What does domain language versioning mean?

It means you keep one stable name for each business concept inside your code, even when partners, vendors, or UI copy use different words. You version those meanings with care and translate outside labels at the edges.

Why should I do this before API versioning?

Because wording changes more often than business rules do. If you protect your internal terms first, a vendor rename or copy update stays in an adapter instead of spreading through models, tests, reports, and docs.

How do I tell a rename from a real new concept?

Ask whether the thing has new rules, different permissions, different pricing, or different reporting. If none of that changed, you likely have a rename, not a new concept.

Where should vendor and partner terms live?

Keep them at the boundary. Map fields like payer_id or current_price_id once in a request mapper, serializer, or adapter, then use your own domain terms everywhere else.

How can a small team start without a big process?

Start with one messy flow such as billing, signup, or orders. Write down the words people use, pick one internal term for each business idea, and record a short definition in the repo.

How long should we keep old names during a rename?

Keep the old and new names side by side for a short, planned overlap. Put a removal date on the old term and make one person own the cleanup so both names do not linger for years.

What if one word means different things to different teams?

Stop and define each meaning in plain language. If product, support, and engineering use one word for different things, pick separate terms and use them consistently in code and docs.

Will this actually make day-to-day work easier?

Yes. Stable terms cut noisy renames, so test names, dashboards, admin screens, and support macros drift less. New teammates also read the code faster because one concept has one name.

What should I review before a release?

Do a quick naming check before release. Ask someone new to explain the main terms in plain words; if they hesitate or two people answer differently, fix the wording before you ship.

When should I ask an outside CTO to review this?

Bring in help when the same naming mess keeps returning across product, API, and support work. A short review with an experienced CTO can sort out the domain terms, boundaries, and cleanup order before the problem spreads further.