DEV Community

Mateus Victor
Mateus Victor

Posted on

Your servers have a runtime. Your customers don't. That's the gap

Why the query model works for analytics and fails for customer health — and what a runtime model looks like in practice.

Most dev founders I know can tell you their p99 latency from memory. Far fewer can tell you how many paying customers got stuck in onboarding this week.

That isn't because they care less about customers. It's because we built infrastructure around continuous evaluation while customer operations remained stuck in the query era.

Every few seconds, Prometheus evaluates alerting rules against incoming metrics. CI pipelines execute automatically for every commit. Monitoring systems don't wait for someone to ask whether a service is healthy. They continuously compute the answer and notify you when something changes.

Customer state works very differently. To understand where a customer is, someone opens an analytics dashboard, checks the CRM, compares billing information, and pieces together events from multiple systems. By the time an answer exists, it's already a snapshot of the past.

The gap between infrastructure monitoring and customer monitoring isn't primarily a tooling problem. It's an architectural one.

Two ways to answer a question

There are two fundamentally different ways to answer questions about a system.

The first is to execute a query when someone asks a question. The system retrieves historical data, computes the result, and returns an answer. This approach is ideal for exploratory analysis. If you want to know how many signups came from Germany during Q2 or which feature grew the fastest over the last month, running a query is exactly the right solution.

I'll call this the query model.

The second approach is to define the important states ahead of time and continuously evaluate them as new events arrive. Instead of computing an answer only when someone asks, the system keeps the answer up to date at all times.

I'll call this the runtime model.

By "runtime," I don't mean a programming language runtime. I mean a system that continuously evaluates business rules and maintains derived state from an incoming stream of events. Prometheus continuously evaluates alerting rules. CI pipelines continuously evaluate every new commit. A customer runtime follows the same idea.

Neither model is universally better. They solve different problems.

The query model excels at answering questions you couldn't predict in advance.

  • How many signups came from Germany last quarter?
  • Which feature grew the fastest this month?
  • Which pricing plan converted best?

The runtime model excels when the question has a stable definition and the answer is operationally important.

  • Is the database healthy?
  • Did the deployment succeed?
  • Is this service available?
  • Is this customer activated?
  • Is this customer at risk?

Those aren't questions you want to investigate manually every hour. You define the conditions once and let the system maintain the answer continuously.

I think customer state belongs in this second category.

What a customer runtime looks like

Imagine you're trying to determine whether a customer has successfully activated.

Your application already emits the relevant events.

  • workspace.created
  • teammate.invited
  • integration.connected

In a query based workflow, someone periodically checks whether each customer has completed those steps. The answer only exists at the moment the query runs. Tomorrow someone needs to repeat exactly the same process.

In a runtime model, the rule is defined once.

A customer becomes Activated after all three events have occurred.

From that point on, the system watches incoming events continuously. The moment the final condition becomes true, the customer's state transitions to Activated. Every consumer, whether it's an API, a dashboard, or an automation, reads exactly the same current state.

This isn't a new architectural pattern. It's the same principle behind state machines, monitoring systems, and CI pipelines. A finite set of inputs produces a deterministic state. New input arrives. State changes. Everyone reads the current truth.

We already use this pattern throughout software infrastructure. We just haven't applied it consistently to customer operations.

Building a customer runtime

If you wanted to build this yourself, the architecture is surprisingly straightforward.

First, you need an event ingestion layer. Your product, CRM, billing provider, and support platform already emit events through webhooks. Receiving them isn't difficult. The real challenge is normalizing those events into a consistent schema that business rules can understand.

Next comes a deterministic rules engine. Every incoming event is evaluated against the rules that depend on it. A completed onboarding step might update activation status, lifecycle stage, health score, and several other pieces of derived state at the same time.

Determinism is essential. Given the same sequence of events and the same rule definitions, the engine should always compute exactly the same result. Without determinism, debugging becomes difficult, versioning becomes unreliable, and trust disappears.

The output isn't another dashboard. It's computed state.

Customer 42 → Activated
Customer 43 → Onboarding
Customer 44 → At Risk
Enter fullscreen mode Exit fullscreen mode

Each state should include evidence showing which conditions were satisfied, which weren't, and which events produced the result.

Finally, those rules need versioning. Customer definitions change over time. Maybe activation requires one more onboarding step. Maybe an at risk customer becomes inactive after 21 days instead of 14. Those changes are business logic, and business logic deserves the same lifecycle as application code. It should be reviewed, tested, deployed, and rolled back through the same engineering workflow.

None of this requires inventing a new architectural pattern. It's simply event driven state computation applied to a problem that has traditionally been solved with dashboards.

Where I landed

I spent years working in Customer Success before moving closer to engineering. One contrast became impossible to ignore.

Application logic lived in Git repositories, pull requests, and CI pipelines.

Customer logic lived in spreadsheets, drag and drop builders, and undocumented processes.

Infrastructure evolved toward deterministic systems.

Customer operations evolved toward dashboards.

That observation led me to build Kite.

Kite ingests events from existing systems and continuously computes customer state from rules written in TypeScript.

import { defineLifecycle } from "@kitesdk/config";

export default defineLifecycle({
  initialState: "new",

  states: {
    activated: {
      enteredWhen: {
        journey: "onboarding",
        status: "complete",
      },
    },

    at_risk: {
      enteredWhen: {
        inactiveDays: 14,
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

The workflow feels closer to shipping infrastructure than configuring a dashboard.

kite init
kite validate
kite deploy
Enter fullscreen mode Exit fullscreen mode

The engine is deterministic. If a customer isn't activated, it doesn't estimate why. It reports exactly which conditions were satisfied, which were missing, and which expected events never arrived.

AI has a limited role. It helps explain missing instrumentation and surface patterns. It never invents evidence or becomes the source of truth.

Whether Kite is the right implementation is almost beside the point. The architectural idea is what interests me most. Customer state feels like something that should be computed continuously instead of reconstructed every time someone opens a dashboard.

A takeaway you can use today

You don't need a runtime to start thinking this way.

Start by defining your customer states explicitly.

Most organizations don't actually have a formal definition of concepts like Healthy Customer, Activated Customer, or At Risk Customer. They have shared intuition.

If three people on your team define activation differently, then activation isn't a definition. It's an opinion.

Write your customer states as deterministic conditions.

Activated = workspace.created AND teammate.invited AND integration.connected within the first seven days.

You can still evaluate that definition manually if necessary. The important change is that you've transformed an implicit idea into explicit business logic.

Once the definition exists, automation becomes an implementation detail instead of a design problem.

Infrastructure stopped querying server health years ago. We defined the rules once and let systems compute the answers continuously.

I think customer state deserves the same treatment.


Top comments (1)

Collapse
 
mateuxcv profile image
Mateus Victor

The hardest part of this approach isn't the engine. It's instrumentation. If your product doesn't emit the right events, no runtime can help you. For teams who've tried event-driven customer tracking: how did you close the gap between what your tools emit and what you actually need to know?