Translate feature brief into domain code before coding
Learn how to translate feature brief into domain code with commands, rules, and events so engineers start with clear behavior and less rework.

Why feature briefs still leave teams guessing
A feature brief tells you what someone wants. It rarely tells you how the system should react, step by step. That gap sounds small, but it causes a lot of confusion.
Take a line like "A customer requests a refund from the billing page." One person imagines an instant refund. Another expects a support review first. A third assumes the app blocks refunds after 30 days. They all read the same brief, and they can all defend their reading.
That happens because stories focus on intent. They capture the user's goal, the business reason behind it, and sometimes a happy path. They usually do not spell out commands, rules, and events. They skip timing, limits, failures, retries, permissions, and handoffs between people and software.
Once the team starts coding, those blanks do not stay blank. Engineers fill them in. Product does too. Designers make assumptions in the UI. QA finds a different set of assumptions later. Support finds the rest when real users hit cases nobody named.
The same gaps show up again and again. What starts the action, and who can trigger it? Which rule allows or blocks it? What does the system record after it happens? What should happen if something fails or arrives twice?
These details often surface late because the brief feels complete on first read. It sounds clear until someone asks, "What if the payment already settled?" or "Does the user get notified before review or after approval?"
That is why teams should translate a feature brief into domain code before engineers open the editor. Not code in a programming language. Domain code in business language: a command someone sends, a rule the system checks, and an event the system records.
Without that step, teams build from guesses. Some guesses will be smart. Some will be expensive. The cost shows up as rework, bug fixes, changed tests, and tense meetings after QA or support reports behavior nobody agreed on.
What domain code means in plain words
Domain code is the part of software that describes how the business works. It is not the page layout, button color, or form field order. It is the behavior behind actions people care about, like placing an order, pausing a subscription, or asking for a refund.
When a team translates a feature brief into domain code, the conversation shifts away from screens. A brief that says "add a refund button and confirmation modal" skips the real issue. The real issue is what a customer is asking the business to do, what the business allows, and what fact the system should record.
A command is the request. It tells the system what someone wants to happen. Names like RequestRefund, PauseSubscription, or ApproveDiscount work because non-engineers can read them and understand them.
Rules decide whether the system accepts or rejects that command. They are business checks. A refund might be allowed only within 14 days, only for paid invoices, and only if the account has no fraud flag. If a rule fails, the system should say why.
Events are facts about the past. They record what already happened, such as RefundRequested, RefundApproved, or SubscriptionPaused. That matters because teams can test facts, audit facts, and discuss facts without getting dragged into UI details.
Names matter more than most teams expect. If support says "refund request" but engineers write ReversePaymentWorkflow, people start talking past each other. Keep the names close to the words the business already uses in sales calls, support tickets, policy notes, and team meetings.
That small shift saves time. Engineers start with clearer intent, product catches missing rules earlier, and everyone spends less time arguing about what the feature means.
Gather the raw input before you map anything
A feature brief rarely tells the whole story. Product notes live in one doc, support questions sit in chat, and edge cases hide in old tickets. If a team models behavior too soon, those gaps turn into bad assumptions.
Pull every source into one working page before you name commands, rules, or events. Use the brief, call notes, support questions, acceptance comments, and any policy text that changes the outcome. One messy page is better than five tabs nobody reads end to end.
Then mark the language people already use. Look for nouns that name business objects, actions people take, limits that block or allow a step, and dates that change what is allowed. This is the fastest way to translate a feature brief into domain code without guessing.
A short note can reveal a lot. If a brief says "workspace owners can pause billing once per year until the renewal date," you already have most of the raw material. "Workspace owner" tells you who starts the action. "Pause billing" names the action. "Once per year" sets a limit. "Until the renewal date" adds a time rule.
Ask who starts each action and who approves it. Do not assume the same person does both. A customer may request a change, support may confirm details, and finance may approve the last step.
You also need to separate what must happen now from what can wait. Teams often mix first-release behavior with later automation and then write confusing tickets. If unusual cases need manual approval for now, say that plainly instead of pretending the system handles everything on day one.
Watch for conflicts while you gather input. If the brief says one thing and support says another, keep both notes visible until someone decides. Quiet contradictions create more rework than missing fields.
When a sentence still feels vague, stop and rewrite it in plain words. Engineers can code around missing details, but they should not have to invent the business.
Turn one story into commands, rules, and events
Take one action from the story and freeze the rest. If you try to map the whole feature at once, the names get fuzzy and people start arguing about edge cases too early.
A story usually hides several actions. Split them. If the story says a customer can pause, resume, and cancel a subscription, start with only one.
Write the command first. Keep it in verb-plus-object form because that makes it easy to read and easy to test. "Pause subscription" is clear. "Subscription pause flow" is not. It sounds like a project name.
Then write the rules that must be true before the command can run. These are business checks, not UI details. They answer a simple question: when should the system allow this action?
For a small story, the map might look like this:
- User action: customer wants to pause a subscription
- Command: Pause subscription
- Rules: subscription is active, the customer owns it, the next billing run has not started
- Success event: Subscription paused
- Failure outcomes: Subscription already paused, customer not allowed, billing window closed
The event matters because it tells everyone what changed after success. It should read like a fact, not a request. "Subscription paused" works. "Pause accepted" is weaker because it does not say what the system actually did.
Name failures in plain language too. Avoid vague labels like "validation error" when a real reason exists. Engineers can map those names to error codes later, but the workshop should stay readable for product, support, and QA.
Then move to the next action and repeat the pattern. One command, a small set of rules, one success event, then clear failures. That rhythm keeps the conversation grounded. It also gives engineers domain code they can write before they think about screens, endpoints, or database tables.
A simple example: refund request in a SaaS app
A refund request sounds simple until the team asks one basic question: what should the app do, step by step?
A customer opens the billing page, clicks a refund option, and expects a fast answer. The story is short, but the behavior still needs names. To translate the brief into domain code, turn the customer action into a command called RequestRefund. That name tells everyone what the app is trying to do right now.
Then the app checks the rules. The order must exist. The payment must have cleared. The refund window must still be open.
Those rules stop vague arguments later. If the order does not exist, the app should say so. If the payment is still pending, or the refund period ended yesterday, the team can point to a rule instead of debating intent.
When all three checks pass, the app records RefundRequested. This event is a fact, not a placeholder for future work. It means the system accepted the refund request at that moment.
After that, the flow can split. A support worker, finance person, or automatic policy can send ApproveRefund or RejectRefund. Those commands create new facts: RefundApproved or RefundRejected.
If approval leads to an actual payout, record RefundPaid as its own event. That separation is worth keeping. Approval might happen at 10:02, while the payment processor finishes the transfer hours later.
Now imagine a customer writes to support on Friday and says, "I was told my refund was approved. Where is the money?" The event trail answers that quickly. If support sees RefundApproved but not RefundPaid, they know the case is still in progress. If they see RefundRejected, they know the system already made a decision and can explain why.
That is the point of commands, rules, and events. A loose story like "customer asks for a refund" turns into behavior the team can test, discuss, and build without guessing.
Write names people can test and discuss
A good domain name sounds like the business talking to itself. If support says "issue refund" and sales says "upgrade plan," your commands and events should use those same words. Teams move faster when the names already feel familiar.
Names break down when they come from the interface instead of the work. Words like button, modal, tab, and screen belong in design notes, not in domain code. A screen can change next week. The business action usually stays the same.
That is why OpenRefundModal is a weak domain name, while ApproveRefund or RejectRefund gives people something concrete to discuss. One describes a click. The other describes a decision.
Short, narrow commands are easier to test. If someone cannot explain a command in one sentence, it is probably doing too much. ProcessOrder is the classic example. Does it charge the card, reserve stock, send the receipt, or all three?
Clearer names look more like this:
ChargeInvoiceinstead ofProcessBillingSuspendAccountinstead ofHandleUserStatusSendTrialEndingReminderinstead ofRunNotificationsMarkContractSignedinstead ofUpdateDeal
Specific names help each team in a different way. Support can predict what happened. Operations can trace what changed. Sales can check whether a promise matches system behavior. Engineers get fewer follow-up questions because the name already carries the intent.
Read the names aloud before anyone starts coding. A ten-minute review with one person from support, sales, or operations usually catches fuzzy wording fast. If two people give two different meanings for the same command, rename it on the spot.
This small step saves real time later. It also makes automation safer. Clear names give people and tools exact intent to work with instead of forcing both to guess.
Mistakes that send engineers back to the brief
Most rework starts before coding. A brief can sound clear in a meeting, then fall apart when engineers try to turn it into domain behavior. The usual problem is not lack of effort. It is lack of precision.
One fast way to confuse a team is mixing commands and events in the same list. A command is a request, such as "approve refund." An event is a fact, such as "refund approved." If both sit side by side with no separation, nobody knows what should happen first, what the system should decide, or what it should record after the decision.
Rules cause trouble too when people write them like goals instead of checks. "Prevent invalid refunds" sounds fine until someone asks what counts as invalid. A rule needs a clear pass-or-fail condition. "Reject refund requests for invoices older than 14 days" is much better because engineers can build it and QA can test it.
Timing often disappears into vague wording. That creates real bugs. If a customer can request a refund only within 14 days, write that number down. If support must respond within one business day, write that too. Time limits change behavior, permissions, and edge cases.
Three other mistakes show up a lot. Teams bundle several actions into one command. They forget to name the actor for each action. They leave permission rules in a separate document where nobody sees them during the workshop.
Bundled commands are especially costly because they hide failure paths. The refund may pass, but the email may fail. The cancellation may need approval, but the refund may not. Separate commands make those branches visible.
Teams avoid most of this by keeping each story plain: who triggers the action, what command they send, which rule checks it, and which event confirms the result. If any part still feels fuzzy, engineers will end up back at the brief asking the same question twice.
Quick review before engineers start
A brief can look clear and still leave gaps once people start building. The fastest way to catch that is to read the commands, rules, and events with a small mixed group: product, engineering, QA, and someone from support or operations. If only engineers can explain the list, the names are still too close to the screen and not close enough to the business.
Each command should make sense without any mention of buttons, forms, or page flow. A non-engineer should be able to say what "Submit refund request" or "Approve trial extension" means in plain words. If they need the mockup to explain it, the command name is probably weak.
Then check the behavior around each command. A command needs rules that decide when it can run, and it needs a clear success event that tells everyone what happened. If the team can say the command but cannot name the event that proves success, the flow is still fuzzy.
Failure cases matter just as much. Name the ones users will actually notice: duplicate request, expired plan, missing approval, limit reached, account locked. You do not need every technical edge case at this stage. You do need the failures that change what the user sees, what QA tests, and what support has to explain later.
A quick review usually comes down to five questions:
- Can someone outside engineering explain each command in business terms?
- Does every command have rules and one success event?
- Did the team name the failures users will notice?
- Can QA turn the list into test cases without guessing?
- Can support follow the event trail and tell a customer what happened?
That last point gets missed a lot. Support does not need deep system detail, but they do need a clean story: request received, eligibility checked, action approved, customer notified. When the event trail reads like that, engineers usually have enough clarity to start building with fewer surprises.
What to do next with your team
Start with one feature that already creates friction. Pick something people keep reading in different ways, like refund requests, access changes, or plan upgrades. Those features expose hidden assumptions fast, which makes them the best place to try this method.
A short review is enough. Put product, engineering, and support in the same room for 30 minutes and work from the brief, not from code. Support usually knows where users get confused, product knows the intent, and engineers can spot missing behavior before anyone builds the wrong thing.
A simple agenda works well. Read one story out loud and agree on the user goal. Write the command the user or staff member triggers. List the rules that must hold true. Name the event that proves the action happened. Mark any open question that blocks delivery.
Keep the first draft small. One page is often enough. If you try to capture every edge case on day one, the team will stall and slip back into vague brief language.
After the feature ships, revise the document. Compare the planned commands, rules, and events with what actually happened during delivery, QA, and support tickets. That cleanup matters because it turns a one-off exercise into a team habit.
If you want to translate a feature brief into domain code without slowing the team down, reuse the same format every time. Keep the structure boring: story, command, rule, event, open question. Consistency makes planning easier, and people stop arguing about the shape of the document.
Some teams need an outside reviewer for the first few rounds. That can help when briefs are messy, roles overlap, or nobody owns the final wording. Oleg Sotnikov reviews product and engineering decisions in this area as a fractional CTO, and the work on oleg.is is closely tied to making delivery clearer before coding starts.
Try this on one confusing feature this week. If the meeting ends with fewer open questions and clearer names, keep the format and use it on the next brief.
Frequently Asked Questions
What does domain code mean?
Domain code describes business behavior, not screen details. It turns a vague story into clear actions, checks, and facts the team can discuss and build.
Why is a feature brief not enough on its own?
A story tells you what the user wants, but it often leaves out timing, permissions, failures, and handoffs. When those details stay fuzzy, engineers, QA, and support fill the gaps in different ways.
When should we translate a brief into commands, rules, and events?
Do it before anyone starts coding. A short workshop early saves rework later because the team agrees on behavior before people make technical choices.
What is the difference between a command, a rule, and an event?
A command is a request like RequestRefund. A rule checks whether the system should allow it, like "the payment cleared". An event records a fact after success, like RefundRequested.
How detailed should the rules be?
Write only the rules that change the outcome. Include limits, dates, permissions, and checks that block or allow the action, but skip UI details like button text or modal flow.
How should we name commands and events?
Use the words your business already uses in support tickets, sales calls, and policy notes. Names like ApproveRefund or PauseSubscription work better than vague labels like ProcessFlow.
What mistakes cause the most confusion?
Skip mixed lists where requests and facts sit together. Also avoid bundled commands, vague rules like "prevent invalid refunds," and missing actors such as who requests, reviews, or approves the action.
Who should review this before engineers build it?
Bring product, engineering, QA, and one person from support or operations. That group catches fuzzy wording fast because each person sees different gaps.
How do we handle approvals or manual steps?
Say the manual step out loud and name it in the flow. If support or finance must review a case first, keep that as a real command and event instead of pretending the app handles everything now.
What is the easiest way to start using this with my team?
Start with one feature that people keep reading in different ways, such as refunds or plan changes. If your team wants outside help on product behavior and delivery, an experienced fractional CTO can review the wording and tighten the flow before coding starts.