C.W.K.
Stream
Lesson 05 of 05 · published

One Backend First, No Silent Fallback

~12 min · embed-engine, fallback, backend, diagnostics

Level 0Reel Novice
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"libmpv owns v0 playback. A second backend requires a failing golden-corpus class, an explicit selection rule, diagnostics, and parity tests; fallback cannot be silent."

The 'Just In Case' Second Engine

It feels responsible to wire in a backup engine. "We'll use libmpv, but if it ever struggles, we'll quietly fall back to AVFoundation." That sentence sounds safe and is quietly poisonous. Now the app has two code paths for every file, twice the surface to test, and — worst of all — a hidden switch that can decide, without telling anyone, which engine the user is actually watching.

Ashen Reel ships one backend: libmpv. Not because a second is forbidden forever, but because a second backend has to earn its way in, and on cutover day nothing had earned it.

The Price of a Second Backend

The invariant sets a high, specific bar. A second backend requires all of: a named failing golden-corpus class (a real file libmpv genuinely can't handle), an explicit selection rule (not a catch-all), parity tests proving the second backend does better on exactly that class, and diagnostics that show which engine ran. Miss any one and the answer is no.

A fallback must be visible and evidence-gated, never a silent catch. Two paths that cover different, proven failure modes are deliberate engineering. A hidden 'if it breaks, try the other one' is how you ship a player that lies about what it's doing.

Here is the difference between an evidence-gated selection and a silent fallback, in code:

A silent switch that reports false state is worse than a hard failure. If AVFoundation quietly took over and the diagnostics still said 'libmpv,' every parity comparison, every bug report, every color claim would be measuring the wrong engine. When a backend can't honestly play a file, the honest outcomes are a visible error and a labeled fallback — never a quiet substitution wearing the wrong name tag.

Why It Shipped With Just One

Here's the punchline: on the 1.0.0 cutover, no required corpus class failed libmpv. Dolby Vision was the one open question, and rather than pre-build an AVFoundation path 'just in case,' Ashen Reel left it as a conditional gate — no failing class, no second backend. The discipline paid a real dividend: one engine, one code path, one thing to trust. You add the second backend the day a real file forces you to, with evidence in hand — and not one day sooner.

Code

Evidence-gated selection vs. the silent fallback that's banned·swift
enum Backend { case libmpv, avfoundation }

/// A second backend is a DECISION with evidence, never a silent catch.
/// It needs a named failing class with a parity proof; the choice is
/// recorded so diagnostics show which engine actually ran.
func selectBackend(for media: MediaClass,
                   diagnostics: inout Diagnostics) -> Backend {
    if let failing = knownFailingClasses[media], failing.hasParityProof {
        diagnostics.record("backend=avfoundation reason=\(failing.name)")
        return .avfoundation
    }
    diagnostics.record("backend=libmpv")   // the default, always visible
    return .libmpv
}

// BANNED, anywhere in the codebase:
//   do { try libmpvPlay(file) }
//   catch { avfoundationPlay(file) }   // <- silent switch, false state
//
// A fallback you can't see in diagnostics is a lie about what the user
// is watching. As of 1.0.0, knownFailingClasses is EMPTY: no class failed.

External links

Exercise

Look for a try/catch (or equivalent) in your code where the catch block silently does something different — a retry with different settings, a fallback service, a default value. Ask two questions: could someone tell from the logs that the fallback path ran? And is there a real, named case that justifies it, or is it 'just in case'? Rewrite one silent fallback so that it's either removed or made loud and evidence-gated.
Hint
A good fallback answers 'when exactly does this fire, and how would I know it did?' If you can't name the triggering case or see it in diagnostics, you don't have a fallback — you have a place where two behaviors hide behind one name.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.