← Back to list

Temporal Fixed the Timer. Not the System.

Why Temporal solves brittle scheduling bugs while exposing retries, determinism, idempotency, and workflow design mistakes you were already…

Modexa · 2026-03-23 14:31 · 0 claps · 5.9 min read
#temporal #distributed-systems #nodejs #backend #software-engineering
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Temporal Fixed the Timer. Not the System.

Why Temporal solves brittle scheduling bugs while exposing retries, determinism, idempotency, and workflow design mistakes you were already carrying.

Temporal fixes flaky scheduling and timers, but it also exposes retry, determinism, idempotency, and workflow design bugs hiding in distributed systems.

A bad scheduler can make you feel cursed.

Jobs fire twice. Or not at all. A delay queue stalls. A cron worker restarts and forgets what it owed the world. Then you introduce Temporal, replace the fragile timer logic with durable workflows and timers, and suddenly the original scheduling bug is gone. Temporal’s core model is built around durable workflow execution, and its timers are persisted so workflows can sleep for long periods without tying up a process.

And that is usually when the real problems begin.

Because Temporal does not just fix time. It makes hidden system assumptions visible. Workflows are replayed from event history, workflow logic has to stay deterministic, and activities are retried by default. Those are superpowers, but they are also bright lights aimed directly at design flaws your old scheduler quietly masked.

The first win feels magical

The first Temporal migration often starts with one practical goal: “make this thing run reliably later.”

That is exactly the kind of problem Temporal is excellent at. Workflows define the sequence of steps, workers execute workflow and activity code, and durable timers let workflow execution pause for seconds, days, or longer without burning a worker thread the whole time. Temporal explicitly documents timers as resource-light and durable across outages.

So the flaky “send reminder in 48 hours” path finally behaves.

No more fragile DB poller. No more homegrown rescheduler. No more midnight script pretending to be an orchestration engine.

You fix the timer, deploy, breathe, and then support pings you because customers got duplicate reminder emails.

Temporal did not create that bug.

It uncovered it.

Why durable execution reveals uncomfortable truths

Temporal’s model depends on replay. A workflow re-executes its code against recorded event history so the platform can recover state durably. That means the code inside a workflow cannot behave differently on replay than it did the first time. Temporal’s docs describe this as deterministic execution, and the TypeScript SDK runs workflows in a deterministic sandbox for exactly that reason.

That constraint is healthy. It is also merciless.

A legacy scheduler often lets teams get away with:

  • reading current time directly in business logic
  • branching on random values
  • calling external services from the orchestration layer
  • mutating behavior during deploys without versioning strategy
  • assuming a failed step either “did nothing” or “obviously succeeded”

Temporal turns those vague assumptions into explicit choices.

ASCII sketch: what changes

Old scheduler
  app code -> cron/db poller -> maybe run task -> hope state is right

Temporal
  workflow history -> replay workflow code -> schedule activity/timer -> persist event -> continue exactly from history

That second model is stronger. It is also less forgiving of hand-wavy engineering.

Bug one: your side effects were never truly idempotent

This is the classic one.

You used to trigger an email, payment update, webhook, or database write from code that “probably only ran once.” With Temporal, external work belongs in Activities, and Activities retry automatically by default when they fail. Temporal documents automatic Activity retries as standard behavior governed by retry policy.

That is fantastic for transient failures.

It is terrible for sloppy side effects.

If your “send invoice” activity times out after the provider accepted the request but before your worker recorded success, Temporal may retry the activity. If the downstream system is not idempotent, congratulations: your reliable scheduler just proved your integration was unreliable all along. Temporal’s docs emphasize that Activities are where failure-prone operations live, which is precisely why idempotency matters there.

Bug two: you were depending on nondeterministic workflow logic

You might be wondering, “Can I just keep some simple logic in the workflow?”

Yes, but only if it is deterministic.

Temporal’s TypeScript docs are blunt: workflow logic is constrained by deterministic execution requirements, and workflows run in a sandboxed environment that cannot freely rely on Node.js or DOM APIs. Non-deterministic changes can cause workflow task failures, and Temporal notes that when a deployment introduces non-determinism, existing workflows can stay open and keep retrying workflow tasks until you fix the code.

That means things like this are dangerous inside workflow code:

// Bad idea inside a workflow
if (Math.random() > 0.5) {
  // branch differently on replay
}

Or:

// Also bad inside workflow logic
const now = Date.now();

The lesson stings a little: many scheduling systems “worked” because they were never forced to replay their past decisions.

Temporal forces the issue.

Bug three: your retry boundaries were emotionally, not technically, designed

A surprising number of systems have retry behavior that sounds like this:

“Retry the whole thing a few times and see what happens.”

Temporal splits concerns more clearly. Activities retry by default; workflows generally do not, and Temporal’s docs say workflow retry policies are uncommon because workflow failures usually indicate code bugs or bad input rather than transient external issues.

That design exposes a common mistake: teams often put too much real-world failure inside a giant orchestration block.

If the whole process must restart because one HTTP call failed, your boundaries are off. If a workflow retry would repeat business decisions, your boundaries are off. If every activity shares one retry strategy, your boundaries are off.

Temporal does not invent these architecture flaws. It just stops hiding them.

Bug four: your long-running workflows were quietly becoming history monsters

Temporal stores workflow event history, and that history is what makes replay and recovery possible. Events are created by the Temporal service in response to external occurrences and workflow commands. Over time, that history can grow, which is why SDK guides emphasize patterns like Continue-As-New for long-lived workflows.

This is where many “simple scheduling” systems reveal a deeper truth: the business process was never simple.

A reminder engine becomes a months-long customer lifecycle coordinator. A subscription renewal flow becomes a state machine with pauses, signals, updates, retries, and exceptions. A scheduled job turns into a living process.

Temporal can model that well. But once you see the full lifecycle, you may realize you were stuffing a workflow-shaped business process into a timer-shaped box.

Bug five: your operational model assumed invisibility was safety

Temporal has a very explicit model of tasks, workflow state transitions, retries, signals, queries, and updates. Workflows can behave like stateful services that receive messages, and task failures are observable in a way homegrown schedulers rarely are.

That transparency is a gift.

It is also uncomfortable.

Because now product asks:

  • Why did this activity retry seven times?
  • Why is this workflow still open?
  • Why did the customer send an update after the timer started?
  • Why does this compensation path exist in three places?

Your old scheduler answered none of these questions. It simply failed more quietly.

What a healthier Temporal design usually looks like

The teams that do well with Temporal tend to make a few mindset shifts.

Keep workflows deterministic and decision-focused

Use workflows for orchestration, state transitions, timers, and durable sequencing. Keep external side effects in activities. Temporal’s docs separate these roles very clearly.

Treat activity idempotency as a requirement, not a wishlist item

Because retries are normal, not exceptional.

Design retry policies per failure mode

A payment authorization, an email send, and a warehouse sync should not all share the same recovery story. Temporal provides declarative retry policy controls specifically for this reason.

Plan for workflow evolution

Long-running workflows survive deployments. That means code changes have to respect replay and versioning realities. Temporal’s task documentation makes clear that bad nondeterministic deployments can strand open workflows until the code is fixed.

The architecture flow nobody tells you about

Here is the real migration story:

Temporal fixes timer durability
    ->
duplicate side effects appear
    ->
workflow replay exposes nondeterminism
    ->
activity retry semantics force idempotency work
    ->
history growth reveals long-lived process design
    ->
message handling exposes state model gaps

In other words, Temporal solves the obvious bug first.

Then it starts telling the truth about the rest of the system.

The takeaway

Temporal is not just a better scheduler.

It is a better mirror.

Yes, it can absolutely fix the maddening class of bugs caused by fragile timers, cron drift, worker restarts, and lost in-memory state. Its workflow model, durable timers, and replay-based execution are built for that.

But once you adopt it, you lose the luxury of vague orchestration. Retries become real. Determinism becomes non-negotiable. Side effects have to be idempotent. Workflow boundaries have to make sense. Long-running state stops being an accidental byproduct and becomes a first-class design problem.

And honestly, that is the bargain.

Temporal fixes the scheduling bug. Then it shows you the system you actually built.

If this felt painfully familiar, leave a comment and follow for more deep dives into the production bugs that only show up after the “fix” works.


메타데이터
post_id
cfdfd597fa2d
slug
temporal-fixed-the-timer-not-the-system-cfdfd597fa2d
url
https://medium.com/@Modexa/temporal-fixed-the-timer-not-the-system-cfdfd597fa2d
canonical_url
https://medium.com/@Modexa/temporal-fixed-the-timer-not-the-system-cfdfd597fa2d
author_url
https://medium.com/@Modexa
status
ok
fetched_at
2026-06-10 12:26:30