DEV Community

FalahMsi
FalahMsi

Posted on

Why notification timing should be deterministic!?

Notification timing looks trivial at first glance.

You have an event.
You subtract a few minutes.
You schedule a notification.

Simple… right?

In practice, notification timing is one of those problems that quietly accumulates edge cases until your system becomes unpredictable, inconsistent, and hard to reason about.

This post explains why notification timing must be deterministic, what usually goes wrong, and how treating it as a pure computation problem changes everything.

The illusion of simplicity

Most applications start with logic like this:
• Find the next event
• Schedule a notification X minutes before it
• Label it as today, tomorrow, or later

This works… until it doesn’t.

As soon as your app grows, you run into questions like:
• What happens when the trigger time crosses midnight?
• Should “today” be based on the event time or the notification time?
• What if the user changes timezones?
• What if the reference time is slightly different across platforms?
• Why does Android fire the notification today while iOS labels it tomorrow?

These issues don’t come from bugs.
They come from non-deterministic semantics.

The core mistake: timeline-based reasoning

A common anti-pattern looks like this:

events.first { isSameDay($0.startTime, tomorrow) }
Enter fullscreen mode Exit fullscreen mode

This feels intuitive — but it’s fundamentally flawed.

Why?

Because it mixes absolute time with human calendar concepts (today / tomorrow) too early.

Once you do that:
• Edge cases multiply
• Different platforms diverge
• “Fixes” become patches instead of rules

Determinism changes the model

A deterministic system answers one question consistently:

Given the same inputs, will every platform produce the same output?

For notification timing, that means:
• No reliance on UI concepts
• No implicit “today” logic
• No platform-specific date helpers
• No stateful or hidden behavior

Only pure computation.

A deterministic contract

Here’s the mental model that works:
1. Select the upcoming event
• Based only on absolute time
• event.startTime > referenceTime
• Sorted by startTime
2. Compute trigger time
• triggerTime = event.startTime - leadTime
3. Derive day label
• Compare calendar days of:
• triggerTime
• referenceTime
• In a specific timezone
• Result: today, tomorrow, or later

That’s it.

No guessing.
No heuristics.
No shortcuts.

Why trigger time matters more than event time

Here’s a subtle but critical insight:

Day labels must be based on the trigger time, not the event time.

Example:
• Event starts at 00:30
• Lead time is 60 minutes
• Trigger fires at 23:30 the previous day

If you label based on the event time, you’ll say tomorrow.

If you label based on the trigger time, you’ll say today — which matches user expectations.

This single rule eliminates a large class of bugs.

Cross-platform consistency is not optional

If your logic lives:
• partly in Swift
• partly in Kotlin
• partly in JavaScript

…then determinism is mandatory, not a nice-to-have.

Otherwise:
• Notifications fire at different times
• Tests pass on one platform and fail on another
• Bugs become “platform quirks”

A deterministic engine guarantees:
• Same inputs
• Same outputs
• Everywhere

Treat notification timing as a pure engine

The key architectural decision is this:

Notification timing logic should be headless, stateless, and pure.

It should:
• Not schedule notifications
• Not know about permissions
• Not touch UI
• Not persist anything

Its job is to answer exactly one question:

What is the next upcoming event, and when should I notify the user?

Everything else belongs to the application layer.

A practical implementation

I ran into these problems repeatedly in production scheduling systems, so I extracted the logic into a small deterministic engine:

Notification Intelligence Engine (NIE)
• Cross-platform (Swift, Kotlin, TypeScript)
• Pure computation
• Timezone-aware
• Backed by shared test vectors
• Identical semantics across platforms

GitHub:
👉 NIE

Even if you don’t use it, the design principles are what matter.

Final thought

Most notification bugs aren’t about notifications.

They’re about time semantics.

Once you make notification timing deterministic:
• Bugs disappear
• Tests become meaningful
• Cross-platform behavior aligns
• The system becomes easier to reason about

If your app depends on notifications, determinism isn’t overengineering - it’s correctness.

programming

architecture

notifications

crossplatform

Top comments (0)