DEV Community

Cover image for Why scheduling tools get availability wrong (and how to fix it)
Asad Yasin
Asad Yasin

Posted on

Why scheduling tools get availability wrong (and how to fix it)

Most scheduling tools work the same way under the hood.

They take your available hours, divide them by a slot duration, and hand you a grid of fixed times. 9:00, 9:30, 10:00, 10:30. Pick one.

It works. But it has a fundamental flaw.

The grid assumption

When you pre-generate slots, you make a hard assumption: that every slot boundary is equally valid. But real calendars don't work that way.

Say you have a rule: available 9am–12pm, 30-minute slots. A calendar event ends at 9:45am. The grid-based approach blocks the entire 9:00 and 9:30 slots, even though there's a valid 30-minute window from 9:45–10:15 that the guest could book.

You've just hidden availability that genuinely exists.

Free windows instead of fixed slots

A better approach: compute free windows, not pre-generated slots.

Instead of dividing your schedule into a grid upfront, you:

  1. Start with your defined available hours as a continuous interval
  2. Subtract all busy time — calendar events, existing bookings, buffers, manual blocks, holidays
  3. What remains is a set of free windows — open intervals of varying length
  4. At booking time, check: does the requested duration fit entirely inside one of these windows?

This means a guest can start at 9:45, 10:00, 10:15 — any 15-minute mark where their chosen duration fits inside an open window. Not just the pre-generated grid positions.

The implementation difference

Grid approach (simplified):

def get_slots(available_start, available_end, duration_mins):
    slots = []
    current = available_start
    while current + timedelta(minutes=duration_mins) <= available_end:
        if not is_busy(current, current + timedelta(minutes=duration_mins)):
            slots.append(current)
        current += timedelta(minutes=duration_mins)
    return slots
Enter fullscreen mode Exit fullscreen mode

Free-window approach:

def get_free_windows(available_start, available_end, busy_periods):
    # Start with the full available interval
    free = [(available_start, available_end)]

    # Subtract each busy period
    for busy_start, busy_end in busy_periods:
        free = subtract_interval(free, busy_start, busy_end)

    # Return windows large enough for at least one slot
    return [(s, e) for s, e in free 
            if (e - s).seconds >= MIN_DURATION_MINS * 60]
Enter fullscreen mode Exit fullscreen mode

The guest-facing slot picker then steps through each free window in 15-minute increments, showing any start time where their chosen duration fits completely inside the window.

Why this matters in practice

The difference becomes obvious on heavy calendar days.

With a grid: a 90-minute meeting ending at 11:30 wipes out the 11:00 and 11:30 slots even if there's a clear 30-minute window from 11:30–12:00.

With free windows: that 11:30–12:00 window surfaces correctly.
The guest sees it. They can book it.

For hosts with back-to-back schedules — the exact people who most need a scheduling tool — the grid approach quietly hides valid availability. Free windows don't.

One more thing: confirm-time safety

Computing free windows solves the display problem. But there's a second problem: what if the calendar changes between when the guest loads the page and when they confirm?

The answer is cache-first with confirm-time re-validation:

  • Show availability from a local cache (fast page loads, no live API call on every request)
  • At the moment of confirm, re-fetch the host's calendar live and check the exact interval one more time
  • If something changed, reject with a 409 and let the guest pick again

This means a guest can never successfully book a slot that became unavailable while they were on the page — even if the cache was slightly stale.


These are the core ideas behind how we built availability in Skedvio — a scheduling tool that computes free windows rather than a fixed slot grid. If you're building something similar or just find the problem interesting, I'd love to hear how you've approached it.

Top comments (1)

Collapse
 
frank_signorini profile image
Frank

This is spot on about how most tools