← Build log

When "Zero Dependencies" Meets Reality: The Hidden Costs of Cross-Platform Distribution

Or: How I learned to stop worrying and love Apple Silicon-only releases

You know that moment when you think you’ve solved a problem completely, only to discover you’ve actually just moved it somewhere else? Like when you finally organize your garage by shoving everything into the basement, then realize you still can’t find your drill when you need it.

That’s exactly what happened when we tried to ship our “zero external dependencies” VoiceCatcher build to beta testers.

TLDR

What You’ll Gain:

  • A framework for discovering and handling hidden dependencies in production builds
  • Strategic decision-making process for beta scope vs. engineering complexity
  • Practical approach to architecture mismatch problems in cross-platform apps
  • Time-saving techniques for dependency verification before distribution
  • Bottom Line Value: Avoid the embarrassment of shipping broken builds to beta testers while making smart scope decisions that keep your momentum.

The Problem: When “It Works on My Machine” Goes to Production

We’d spent weeks eliminating external dependencies. No more Homebrew requirements. No more manual Whisper installations. No more environment variable configuration. Our production app was supposed to be completely self-contained—just download the DMG, drag to Applications, and go.

The ARM64 build? Perfect. Tested extensively. All dependencies bundled and verified.

The Intel build? Well, that’s where things got interesting.

A beta tester with an Intel Mac reported that the app “just bounces in the dock and never opens.” No error messages. No crash reports. Just… nothing.

The Investigation: Architecture Mismatch Hell

Here’s what we discovered when we dug into the Intel build:

$ file "VoiceCatcher.app/Contents/Resources/app.asar.unpacked/bin/voicecatcher-capture"
# Result: Mach-O 64-bit executable arm64

$ file "VoiceCatcher.app/Contents/Resources/app.asar.unpacked/whisper/bin/whisper-cli"
# Result: Mach-O 64-bit executable arm64

Both critical binaries were ARM64, packaged inside what was supposed to be an Intel build. The Electron app itself was Intel, but the audio capture and transcription engines? ARM64 only.

No wonder it wouldn’t run.

The Hidden Dependency Discovery Process

This led us to develop what I’m calling the “Hidden Dependency Audit”—a systematic approach to verifying true self-containment:

1. Binary Architecture Verification

# Check every executable in your build
find "YourApp.app" -type f -perm +111 -exec file {} \;

2. Dynamic Library Dependency Mapping

# For each binary, check what it expects to find
otool -L "path/to/binary"

3. Runtime Path Resolution Testing

# Verify bundled libraries are actually accessible
install_name_tool -add_rpath @executable_path/../lib your-binary

4. Production Environment Simulation

We built a test script that temporarily hides local development dependencies and verifies the app works in isolation.

The Strategic Decision: Scope vs. Complexity

Here’s where it gets interesting from a product management perspective. We had several options:

Option 1: Universal Binaries

  • Build both ARM64 and Intel versions of all binaries
  • Combine with lipo into universal binaries
  • Cost: 2x file size, complex build process, cross-compilation setup
  • Benefit: True cross-platform support

Option 2: Cross-Compilation Toolchain

  • Set up Intel build environment on ARM64 Mac
  • Use GitHub Actions with Intel runners
  • Cost: CI/CD complexity, slower iteration, testing challenges
  • Benefit: Native Intel binaries

Option 3: ARM64-Only Distribution

  • Ship only Apple Silicon builds for beta
  • Clear documentation about requirements
  • Cost: Smaller addressable market for beta
  • Benefit: Immediate shipping, zero additional complexity

The Framework: Beta Scope Decision Matrix

When facing this kind of scope vs. complexity decision, I use this framework:

1. Market Reality Check

  • Apple Silicon adoption: M1 launched November 2020 (3.5+ years ago)
  • New Mac sales: Virtually all Apple Silicon since 2021
  • Beta tester demographics: Likely skew toward newer hardware

2. Engineering Velocity Impact

  • Universal binaries: 2-3 weeks additional work
  • Cross-compilation: 1-2 weeks setup + ongoing maintenance
  • ARM64-only: Ship today

3. Risk Assessment

  • Shipping delay: High cost for momentum
  • Beta feedback quality: More important than coverage
  • Technical debt: Can address Intel support post-beta

4. Rosetta 2 Reality

Intel Macs could run ARM64 apps via Rosetta 2, but:

  • Requires separate installation (breaks “zero dependencies”)
  • Audio processing compatibility concerns
  • Performance implications for real-time transcription

The Decision: Strategic Scope Reduction

We chose ARM64-only for beta distribution.

Why this was the right call:

  • Momentum preservation: Ship to beta testers this week, not next month
  • Quality focus: Better to have excellent ARM64 support than mediocre cross-platform
  • Market alignment: Most beta testers likely have Apple Silicon anyway
  • Feedback optimization: Focus testing on primary architecture

How we communicated it:

  • Clear “Apple Silicon Required” in all beta communications
  • Roadmap commitment to Intel support post-beta
  • Transparent about the technical trade-offs

The Implementation: Dependency Bundling Done Right

For the ARM64 build, we solved the original dependency problem completely:

1. Dynamic Library Bundling

Our setup script now copies all required .dylib files:

// Copy all Whisper dependencies
const requiredDylibs = [
  'src/libwhisper.1.dylib',
  'ggml/src/libggml.dylib',
  'ggml/src/libggml-cpu.dylib',
  // ... 7 total libraries
];

2. Runtime Path Configuration

install_name_tool -add_rpath @executable_path/../lib whisper-cli

3. Verification Testing

Production test script that verifies:

  • All bundled dependencies exist and are correct architecture
  • No system dependencies interfere
  • Binary path resolution works correctly
  • Environment variable isolation functions

The Human Reality: When Perfect Becomes the Enemy of Good

Here’s what I learned about myself as a builder: I have a tendency to want to solve every problem completely before shipping anything. The “zero dependencies” goal became an obsession that almost derailed our beta timeline.

The breakthrough moment came when I realized that “zero dependencies for our target users” was different from “zero dependencies for every possible user.”

Sometimes the most strategic decision is to consciously narrow your scope to maintain momentum. Perfect cross-platform support can wait if it means the difference between shipping this week and shipping next month.

The Business Impact: Momentum vs. Market Coverage

Quantifiable benefits of our decision:

  • Time saved: 2-3 weeks of engineering work
  • Beta timeline: On track instead of delayed
  • Quality improvement: Focused testing on primary architecture
  • Technical debt: Manageable and addressable post-beta

ROI analysis:

  • Cost of delay: Lost momentum, beta tester engagement
  • Cost of complexity: Engineering time, testing overhead, maintenance burden
  • Benefit of focus: Higher quality feedback, faster iteration

When to use this approach:

  • Early beta phases where feedback quality > coverage
  • Resource-constrained solo building
  • When architectural complexity significantly impacts timeline

What This Means for Us

This experience reinforced something I’ve learned over 25+ years in tech: the best product managers know when to ship 80% of the solution to 80% of the market, rather than 100% of the solution to 100% of the market six months later.

For solo builders especially, momentum is everything. Every week you spend solving edge cases is a week you’re not getting feedback from real users. Every perfect solution you build in isolation is a solution that might be solving the wrong problem.

The Intel Mac users who need VoiceCatcher? They’ll still be there in two months when we add Intel support. The beta testers with Apple Silicon who are ready to give us feedback today? They won’t wait around while we perfect cross-platform compatibility.

What doors this opens for solo builders:

  • Permission to ship imperfect solutions to perfect audiences
  • Framework for making scope vs. complexity trade-offs
  • Systematic approach to dependency verification
  • Strategic thinking about beta distribution

Sometimes the most strategic product decision is knowing what not to build right now. In our case, that meant choosing Apple Silicon-only distribution to preserve momentum and focus our limited engineering resources on the core experience.

The hidden dependencies taught us something valuable: true “zero dependencies” isn’t just about bundling libraries—it’s about understanding your entire execution environment and making conscious decisions about what complexity you’re willing to take on.

The bottom line: We shipped a completely self-contained VoiceCatcher build to our Apple Silicon beta testers this week instead of a theoretically perfect cross-platform build next month. And honestly? That feels like exactly the right trade-off for where we are in our journey.


Building VoiceCatcher has been a masterclass in product management trade-offs. Want to follow along with more behind-the-scenes decisions like this? The messy middle of solo building is where the real learning happens.