DayPilot
FeaturesPricingAboutContact
DayPilot

The all-in-one work management platform for modern teams. Plan, collaborate, and ship work faster.

Product

  • Features
  • Pricing
  • Changelog
  • Roadmap
  • Integrations

Company

  • About
  • Blog
  • Careers
  • Press

Support

  • Contact
  • Documentation
  • Status
  • Community

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Security

© 2026 DayPilot, Inc. All rights reserved.

All systems operational
⚡
Back to Blog
engineering 9 min read

How We Built Real-time Sync Without Melting Our Database

A deep dive into the architecture behind DayPilot's real-time collaboration — Supabase channels, optimistic updates, and the conflict resolution strategy that keeps 10,000 concurrent users in sync.

MC

Marcus Chen

Staff Engineer · May 10, 2026

Engineering Supabase Real-time Architecture

When we decided to make DayPilot fully real-time, we knew the engineering challenge was significant. This post walks through the decisions we made, the mistakes we survived, and the architecture we landed on.

Why real-time is hard

Real-time collaboration sounds simple: when Alice moves a task, Bob sees it move. But the moment you have more than two users, you hit fundamental distributed systems problems — network partitions, ordering guarantees, and conflict resolution.

Our starting point: polling

Our first implementation was embarrassingly simple: poll the database every 3 seconds. It worked for our first 500 users, but as we scaled, the load became unsustainable. At 5,000 concurrent users, our Supabase instance was getting 100,000 SELECT queries per minute just from polling.

typescript
// The naive approach — don't do this
useEffect(() => {
  const interval = setInterval(async () => {
    const data = await fetchAllTasks(boardId);
    setTasks(data);
  }, 3000);
  return () => clearInterval(interval);
}, [boardId]);

Switching to Supabase Realtime

Supabase's Realtime engine uses PostgreSQL's logical replication to stream changes to connected clients. Instead of polling, clients subscribe to a channel and receive row-level change events.

typescript
const channel = supabase
  .channel(`board:${boardId}`)
  .on('postgres_changes', {
    event: '*',
    schema: 'public',
    table: 'tasks',
    filter: `board_id=eq.${boardId}`,
  }, (payload) => {
    handleRealtimeEvent(payload);
  })
  .subscribe();

Optimistic updates and conflict resolution

The hardest part wasn't subscribing to changes — it was handling the 200ms window between a user's action and the server confirmation. We needed updates to feel instant while also being correct.

  • Apply change optimistically to local state immediately
  • Send mutation to the server
  • On confirmation: no-op (state already correct)
  • On error: revert with a toast explaining what happened
  • On concurrent conflict: last-write-wins with version vectors

The rule we live by: the UI must never lie to the user for more than 200ms.

Performance result

After switching to Supabase Realtime with optimistic updates, our database query volume dropped by 94% while delivering a far better user experience.

Ready to try it?

Start managing work the right way

Join thousands of teams already using DayPilot to stay aligned and ship faster.

Get started free More articles