AppKit, Beachballs, and the Death of Invisible Magic

Today was one of those days where you don’t add a feature.
You remove a ghost.

The Symptom

BakBeat wasn’t crashing.

It wasn’t throwing errors.

It wasn’t even “slow” in a traditional sense.

But there was something worse:
A momentary, uncomfortable flicker when switching artists.

And earlier in the week? A brief main-thread stall. Not catastrophic. Just enough to feel wrong.

If you’ve built desktop software long enough, you know that feeling.

The beachball isn’t here yet.

But it’s thinking about it.

That’s when you stop shipping features and start interrogating reality.


Phase 1: Main Thread Beachball Debugging

We started where you always start:

  • Is the main thread blocked?

  • Is something synchronous that shouldn’t be?

  • Is state bouncing more than it should?

Instrumentation went in.

We logged:

  • Artist selection timestamps

  • Album load task start

  • Snapshot publication

  • AppKit table reconfiguration

  • No-op vs reload paths

 

What we learned:

  • The database wasn’t slow.

  • Fetch + transform was ~2–8ms.

  • Publishing was negligible.

  • The first interaction after launch had a cold-start delay.

  • After that? Snappy.

No mystery 2-second query.

No runaway task.

No rogue main-thread disk IO.

That’s important.
Because when you don’t measure, you guess.
And guessing is how you rewrite stable systems for no reason.


Phase 2: SwiftUI vs AppKit (Again)

The albums pane had been SwiftUI-driven.

Declarative.

Diff-based.

Elegant.

But also…

  • Hard to reason about when and why it re-rendered.

  • Prone to subtle “stale frame” windows.

  • Too eager to help.

You’d click a new artist, and for a split second, you’d see albums from the previous one before state caught up.
Technically correct.
Visually uncomfortable.

So we moved the heavy pane to AppKit.

Not the whole app. Just the part that needed:

  • Deterministic reload control

  • Explicit selection behavior

  • Stable table lifecycle

  • Zero hidden diff magic

And something interesting happened.

The UI stopped feeling fragile.

SwiftUI could re-evaluate ten times.

 

AppKit would calmly say:

  • “Nothing changed.” → No-op.

  • “Selection changed.” → Update selection only.

  • “Albums changed.” → Reload.

That’s control.


Phase 3: Killing the Flash

We added three simple rules:

  1. Immediately invalidate albums when artist changes.

  2. Cancel previous load tasks deterministically.

  3. Guard publication so stale async results can never overwrite current state.

Then we added diffing inside the AppKit controller so that:

  • Identical data = no reload.

  • Selection-only changes = no full reload.

After that?
The flash disappeared.

The first-click cold-start delay is still measurable — but predictable.

And predictable systems are debuggable systems.


What We Didn’t Do

We didn’t:

  • Add speculative caching.

  • Add placeholder state hacks.

  • Rewrite the data layer.

  • Over-engineer preload pipelines.

Because it wasn’t a data problem.

It was a state ownership problem.

And state ownership is architectural.


Why AppKit Feels “Better” Here

This isn’t anti-SwiftUI.

SwiftUI is excellent for:

  • Layout composition

  • Structure

  • Simple state flows

But for dense, high-frequency data panes with selection and async churn?
AppKit gives you something invaluable: You decide when work happens.

There’s no invisible diff engine guessing intent. There’s no surprise invalidation cascade.
Just:

Input → Compare → Act (or don’t)

That aligns with the BakBeat philosophy.


The Philosophy (Again)

BakBeat isn’t trying to be flashy.

It’s trying to be:

  • Deterministic.

  • Boringly reliable.

  • Resistant to invisible behavior.

When a user clicks an artist, the system should:

  • Clear previous state immediately.

  • Load new data.

  • Never briefly show something wrong.

  • Never freeze without explanation.

Today, it does that. And more importantly:

We understand why.


What’s Next

We’re not prematurely optimizing the first-interaction delay.

We instrumented it.

We measured it.

We understand it.

 

If it becomes user-visible or annoying, we’ll address it surgically. Until then?

We move forward.

Because stability is more important than cleverness.

And invisible magic doesn’t ship reliable software.

— BakBeat Labs