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

swift build Is Not Deployment Proof

~12 min · self-contained, deployment, dylib, build

Level 0Reel Novice
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"A successful swift build is not deployment proof."

The Most Expensive Green Checkmark

You run swift build, it succeeds, and something in your brain files the work as done. For a native app that embeds a big C library, that instinct is a trap with a delay on it. A green build proves the code compiled and linked in your environment. It says nothing about whether the app will even launch on a Mac that isn't yours.

The reason is where the linker found libmpv. On your development machine, Homebrew has it sitting at a tidy path, and the build happily links against it. Ship that exact binary to a clean Mac with no Homebrew, and it dies on launch looking for a dylib that isn't there. The build was honest — it linked what it found. What it found just doesn't travel.

A green build is a promise; the installed artifact is the proof. Compiling and linking is one phase; producing something that runs on the target is another. Don't let the satisfying checkmark of the first convince you you've done the second.

You can see the trap in one command — ask the built binary where it thinks its dependencies live:

'Works on my machine' is a native-app disease, not just a joke. Your Mac is the most contaminated test environment you own: it has your Homebrew, your tools, your paths, your certificates. The whole point of a self-contained bundle is to stop depending on that contamination. Test on the clean target, or you're testing your own machine's mess.

Build, Release, Run Are Three Different Things

The cleanest mental model borrows from the twelve-factor discipline: build, release, and run are separate stages. Build turns source into artifacts. Release packages those artifacts into something self-contained and versioned. Run is that release executing on a real target. Ashen Reel treats them as distinct on purpose — the build makes binaries, a scripted release bundles and signs the whole dependency closure, and the run is proven on the actual installed app. Collapsing them is exactly how a dev-only dylib path sails all the way to a user's launch failure.

Code

The green build hides a dev-only path — one command reveals it·bash
# 'It builds!' -- but where does the binary think libmpv lives?
swift build            # succeeds. feels done. is not done.

otool -L .build/debug/AshenReel | grep -i mpv
#   /opt/homebrew/opt/mpv/lib/libmpv.2.dylib   <-- a DEV-ONLY path
#
# That path exists on YOUR Mac (via Homebrew) and NOWHERE on a clean target.
# swift build proved the code links in your environment. It proved nothing
# about whether the installed app carries libmpv with it. Those are different
# claims, and only the second one is 'deployable'.

External links

Exercise

For something you deploy, list what your build step actually proves versus what deployment requires. Find one dependency your build resolves from your local environment — a system library, an installed tool, an environment variable, a path. Ask: does that dependency travel with the artifact, or is it assumed present on the target? If it's assumed, that's a launch failure waiting for the first clean machine.
Hint
Point your equivalent of 'otool -L' (or ldd, or a dependency inspector) at your built artifact and read what it expects to find at runtime. Every entry that resolves to a path or tool specific to YOUR machine is a deployment gap the build will never flag — because from the build's point of view, everything was right there.

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.