Gabriel Schenker

Gabriel Schenker

Nobody should have to query production to know what a policy did

Nobody should have to query production to know what a policy did

A fictional scene, but a typical one.

Early December, a ticket lands in the support queue, marked urgent. A customer writes that she cancelled her life policy during the cooling-off period, at 23:58 on 30 November, two minutes before the deadline. Her policy is still active, and her premium has been collected again.

A flurry of activity starts. A developer digs deep into the code to work out which rules were applied to her request, and in which order. Someone from operations queries the production database for her policy, her request and whatever else was recorded about it. By lunchtime, three people are on a call comparing query results, and nobody can say for sure what the system decided, or why.

Similar things can happen with premium collections, where a collection silently fails or a customer is charged twice.

With an Event Model, the answer to "what should have happened?" is already on the board, as a scenario anyone can read. Nobody has to dig through code or query production to find it.

Event Modeling is not a fantasy, and it doesn't only work in the ivory tower. It works for typical line-of-business applications in a highly regulated enterprise environment. That's the claim of this article, and I want to back it with one feature on one board: a policyholder cancels a life policy during its cooling-off period, gets the premium back, and the bank confirms or returns the payment.

Over the last weeks I took this feature apart one building block at a time. This time I want to show the whole board at once, and the way of working that makes it possible.

A blueprint we own jointly

The essence is that we have a blueprint that we discuss and build jointly, and that we own jointly.

There is no rule about who can talk to whom. There are no gatekeepers and there is no silo thinking. The compliance officer doesn't have to go through the product owner to ask about a deadline. The developer doesn't need permission to ask the underwriter why a rule exists. Nobody gets to defend some imagined, or even real, privilege, such as being the only one who knows how cancellations really work.

This is more than "invite everyone to the workshop". A workshop ends. The board stays, and everyone keeps working on it and keeps it true. When someone finds a gap three weeks later, they add it to the board. They don't send it to whoever "owns" that part of the system.

Every question is taken seriously

Every question has to be taken seriously. All the rules and all the assumptions have to be put down and made explicit, in a way that everyone can understand and then verify or question:

  • Why is this so?
  • Don't we miss something here?
  • Is that really how we do it, or are we making wrong assumptions?

It's similar to a brainstorming session, where we don't judge or ridicule each other's ideas. In Event Modeling we don't downplay, suppress or ridicule questions and concerns either. The "naive" question is often the one that finds the missing rule. "What if the customer's screen shows a different deadline than the one we recorded?" sounds like a detail. On this board it is a rule of its own, with a name, a scenario and an error, CoolingOffEndMismatch.

In a regulated business this isn't a matter of style. A rule that only lives in one person's head can't be checked by anyone else, and that includes the auditor.

Everything on paper

At the end, we have everything "on paper". Read the board from left to right, along the time axis.

The full cooling-off board in one picture, from PolicyIssued on the left to the bank's answer on the right

The screens and the actors. We have the UI/UX story, as simple sketches of screens or as elaborate Figma designs. The policyholder sees the "Cancel policy" screen with the policy number and the date the cooling-off period ends. The operations team sees a list of every cooling-off cancellation, and a back-office clerk sees the payouts that need attention. We see who looks at what, which data they enter and which activities they trigger.

The business rules. We have the business rules, written as Given-When-Then (GWT) scenarios using the very elements of the board and meaningful example data, so that everyone can discuss them and validate or question them. For a policy whose cooling-off period ends on 30 November at 23:59:

  • Given the policy was issued, when the policyholder cancels on 10 November, then the policy is cancelled in cooling-off.
  • Given the same policy, when the request arrives at 23:59 on 30 November, then it's still cancelled, because the last moment counts.
  • Given the same policy, when the request arrives at 00:00 on 1 December, then it's refused with CoolingOffPeriodExpired.

That's the customer from the opening. Her request at 23:58 on 30 November was in time, and the board says so in three lines that a developer, an operations analyst or a compliance officer can read without opening the code or querying production.

The events and the exceptions. We have the business events that result when the rules are applied successfully, such as PolicyCancelledInCoolingOff and CoolingOffRefundIssued. We have the exceptions when they aren't: PolicyNotFound, PolicyAlreadyCancelled, CoolingOffEndMismatch and CoolingOffPeriodExpired. The events are explicit. Nothing happens to a policy that isn't written down as a fact.

The views. We have the views that we build from past events. There is no magic in the views and projections. All the data has a clear origin in well-understood events. The deadline on the cancel screen comes from PolicyIssued. The "days into the window" column on the operations list comes from PolicyIssued and PolicyCancelledInCoolingOff. These views are used directly, either to show something in a UI or as a todo list for an automation. RefundsToIssue is such a list: the cancellation puts a row on it, and the issued refund takes it off.

The integration with other systems. We also see explicitly how our system integrates with other systems. Paying the refund means calling the bank, and that call is an automation: a processor works through a list of payouts to send, calls the bank's API and records what happened. The bank's later answer, for example a returned payment because the account was closed, isn't something we call. It arrives as the bank's event, so it goes through translation. The message lands on a translation todo list, a pure mapping turns it into a fact in our own words, and a worker decides what it means for this refund. The bank's words become our facts, and the decision stays on our side.

Nobody has to guess where a number on a screen came from, or what happens after the click. It's all on the wall.

The time axis instead of orchestration

We have the time axis, which tells us what happens in which order. PolicyIssued comes first. The cancellation comes later, the refund after that, and the bank's answer last.

That's why we don't need complex orchestration. We don't build a central process that knows the whole cancellation flow and steers each step. We flatten everything out along the time axis and make it sequential. Each step reacts to facts that are already on the timeline: the cancellation puts a refund on a todo list, the refund puts a payout on another one, and the bank's answer goes on a third. If one step is down for an hour, the lists still show exactly what's owed.

On the write side, we use Dynamic Consistency Boundaries (DCB) instead of aggregates. A decision doesn't load a big "Policy" object with every rule the business ever invented. It loads only the facts it needs. To cancel, it needs to know whether the policy was issued, when its cooling-off period ends and whether it was already cancelled. That's two event types for one policy. The command that the refund automation sends needs its own small set of facts. Each decision draws its own boundary, so two cancellations of the same policy can't both go through, and nothing else has to be locked.

Four building blocks, self-contained work packages

Everything on this board is built from only four types of building blocks:

Building block On this board
State change The policyholder cancels the policy in cooling-off from the "Cancel policy" screen
State view Policy details for the cancel screen, the operations list, the todo lists
Automation Issue the refunds that are owed, send payouts to the bank and record failed attempts, end cooling-off windows that run out, act on a returned payout
Translation The bank's answer about a payout, such as a returned payment, turned into one of our facts

Because there are only four, we can easily slice the model into nice, repeatable work packages. Each slice is self-contained and information-complete. It names its screen or its trigger, its command if it has one, its events, its rules and the scenarios that define what correct means. A slice can be picked up by a human or by an AI agent and implemented without the need to ask questions, because the answers are already on the board.

That's where joint ownership pays off a second time. All the questions were asked, in the open, before anyone wrote code, so the developer who picks up the automation that issues the cooling-off refund doesn't have to find the underwriter to ask whether a refund can be paid twice. The scenario already says it can't.

The deep dives

Each part of this board has its own article, with the model and the code:

Not an ivory tower

A cooling-off cancellation isn't an exotic use case. It has a regulatory deadline, money going back, a bank on the other side and a compliance officer who wants to know why. That's a normal day in a regulated enterprise, and it fits on one board that everyone in the room can read, question and own.

That's the point. The method isn't clever. It's explicit. Every rule, every assumption and every fact is on the wall, where anyone can ask "Why is this so?" and nobody has to query production to know what a policy did.

Originally published on LinkedIn on 10 Nov 2026: https://www.linkedin.com/pulse/nobody-should-have-query-production-know-what-policy-did-schenker-2okre/