Core Java

Elixir’s “Let It Crash” Philosophy: When Failing Fast Is a Feature

Why the world’s most reliable systems embrace failure instead of preventing it

Picture this: You’re building a system that must not go down. Lives might depend on it. Your instinct says: add more error handling, more validation, more checks. Catch every exception. Handle every edge case. Build walls of defensive code.

Now picture doing the exact opposite: Let it crash. When something goes wrong, don’t handle it—just die and restart.

This sounds insane. Yet this philosophy, inherited from Erlang and embraced by Elixir, powers systems with uptime measured in nines you can count on both hands. We’re talking about 99.9999999% uptime—about 31 milliseconds of downtime per year.

1. The Problem with Defensive Programming

Most programming languages teach you to be defensive. In Java, you write try-catch blocks. In Go, you check every error. In Python, you add exception handlers everywhere. The goal is noble: prevent crashes at all costs.

But here’s what actually happens:

1.1 The Hidden Cost of Error Handling

Research from Stanford found that error handling code contains bugs at rates 2-10 times higher than normal code. Why? Because error paths are rarely tested, often written as afterthoughts, and create exponentially complex state spaces.

Consider this scenario: You make a database call. It might succeed, fail immediately, timeout, return partial data, or throw an exception. For each outcome, you have more code—and each of those branches can fail. You’ve just created a tree of failure states that’s impossible to test comprehensively.

Code TypeBug RateTest CoverageComplexity Factor
Happy path logic1x (baseline)85-95%1x
Error handling code2-10x20-40%3-5x
Nested error handlers5-15x5-15%10-20x

Elixir’s philosophy says: Don’t write that error handling code. Just crash. Let something else worry about recovery.

2. The Supervisor Tree: Architecture of Resilience

The magic that makes “let it crash” work is the supervisor tree. Think of it like a management hierarchy at a company, but one that actually works.

At the bottom, you have worker processes doing actual work—handling requests, processing data, managing connections. These workers are allowed to be fragile. They focus on the happy path and crash when things go wrong.

Above them, you have supervisors whose only job is to watch workers and restart them when they crash. Supervisors don’t do real work—they just supervise. And above those supervisors? More supervisors, creating a tree structure.

2.1 Restart Strategies That Match Reality

Supervisors can restart processes with different strategies, each matching a real-world failure pattern:

One-for-one: If a worker crashes, restart just that worker. This is like having a team where if one person gets sick, you bring in their backup, but everyone else keeps working. Perfect for independent workers like web request handlers.

One-for-all: If any worker crashes, restart all workers under this supervisor. This is like restarting your entire web server when the database connection dies, because all workers share that connection. Sometimes a clean slate for everyone is simpler than managing partial states.

Rest-for-one: If a worker crashes, restart that worker and all workers started after it. This handles dependencies—if process B depends on process A, and A crashes, you need to restart B too.

Key Insight: These strategies aren’t trying to prevent crashes—they’re managing crashes as a normal operational event. The system is designed assuming things will fail, not hoping they won’t.

3. The Nine Nines: Real-World Uptime

Here’s where theory meets reality. Ericsson reported that their AXD301 telecommunications switch, built on Erlang’s “let it crash” philosophy, achieved 99.9999999% availability. That’s nine nines.

Compare this to typical uptime targets:

Uptime %Downtime per YearTypical For
99% (“two nines”)3.65 daysConsumer web apps
99.9% (“three nines”)8.77 hoursStandard SaaS
99.99% (“four nines”)52.6 minutesPremium SaaS
99.999% (“five nines”)5.26 minutesCloud infrastructure
99.9999999% (“nine nines”)31.5 millisecondsTelecom switches (Erlang)

How? Because when a component fails, it fails fast, gets restarted clean, and the rest of the system keeps running. The alternative—systems limping along in corrupted states—causes outages that cascade and take down everything.

3.1 WhatsApp’s Billion Users

WhatsApp famously scaled to 900 million users with just 50 engineers, largely thanks to Erlang’s architecture. Their servers handled 2 million connections each—a number that sounds impossible in languages built on defensive programming.

The secret wasn’t that their code never crashed. It’s that crashes were isolated, automatic recovery was built-in, and engineers could focus on features instead of defensive coding.

4. Why This Feels Wrong to Most Developers

If you come from Java, C#, Python, or most mainstream languages, “let it crash” feels deeply wrong. Your instincts scream at you. Here’s why the culture clash is so profound:

4.1 Different Mental Models

4.2 The Cost of Context Switching

In Java, creating a thread costs ~1MB of memory. Crashing and restarting is expensive. Your instinct to prevent crashes makes sense.

In Elixir, creating a process costs ~2KB of memory. You can have millions of processes running simultaneously. Restarting one takes microseconds. Crashes are cheap.

When crashes are expensive, you write defensive code. When crashes are cheap, you embrace them. The economics drives the philosophy.

5. What “Let It Crash” Actually Means

This philosophy is often misunderstood. It doesn’t mean “write buggy code and hope for the best.” Here’s what it actually means:

1. Handle Expected Errors, Crash on Unexpected Ones

User submitted invalid email format? Handle it gracefully—that’s expected. Database returned malformed data? Crash. Something you didn’t anticipate? Crash. The system knows how to recover from crashes. It doesn’t know how to recover from corrupted state.

2. Isolate Failures

Each process is isolated. When one crashes, it doesn’t take others with it. This is fundamentally different from a shared-memory system where one null pointer dereference can corrupt everything.

Dave Thomas, author of “Programming Elixir,” describes it: “In Elixir, processes share nothing. When something goes wrong in one process, it’s isolated. The rest of the system continues normally.”

3. Design for Recovery, Not Prevention

Instead of asking “how do we prevent this error?” ask “how quickly can we recover from this error?” Recovery is often simpler, faster, and more reliable than prevention.

ScenarioPrevention ApproachRecovery Approach
Database timeoutAdd connection pooling, retry logic, circuit breakers, fallback caches (500 lines)Crash worker, supervisor restarts it with fresh connection (5 lines)
Memory leakFind and fix leak, add monitoring, tune garbage collection (weeks)Restart process periodically, memory is reclaimed (minutes)
DeadlockAnalyze locking patterns, add timeouts, restructure code (days)Process crashes on timeout, restarts clean (already built-in)

6. Adopting the Mindset

You don’t need to use Elixir to benefit from this philosophy. Here’s how to apply it elsewhere:

6.1 Design for Crash Isolation

Use containers, microservices, or separate processes to isolate failures. When one component crashes, it shouldn’t take the whole system down. Companies like Netflix apply this with their Chaos Engineering approach—they deliberately crash components to ensure isolation works.

6.2 Make Restarts Fast and Automatic

Whether using Kubernetes, systemd, or cloud orchestration, ensure crashed processes restart automatically and quickly. The faster restart time, the more viable “let it crash” becomes.

6.3 Externalize Critical State

Don’t keep critical state in memory. Use databases, message queues, or distributed caches. When a process crashes and restarts, it should be able to reconstruct necessary state from external sources.

6.4 Write Simple Code

This is the hardest lesson. Every try-catch block you don’t write is complexity you don’t have. Every error handler you skip is a bug you can’t introduce. Joel Spolsky once noted that the best code is the code you don’t write. “Let it crash” takes this seriously.

7. When Not to Let It Crash

This philosophy has boundaries. Don’t use it when:

  • State can’t be reconstructed: If losing process state means losing critical data (and you can’t recover it), you need different strategies
  • Restart cost is high: If spinning up a new process takes minutes, crashing isn’t viable
  • External resources are scarce: If you’re managing hardware, file handles, or expensive external connections, you can’t just restart
  • Errors are expected business logic: User validation errors aren’t crashes—handle them normally

The philosophy works best for distributed systems handling many concurrent operations where isolation is possible and restarts are cheap.

8. What We’ve Learned

The “let it crash” philosophy inverts traditional thinking about reliability. Instead of preventing failures through ever-more-complex defensive code, it embraces failures as normal events and focuses on fast, automatic recovery.

This approach achieves something remarkable: systems become simpler and more reliable simultaneously. By writing code for the happy path and letting supervisors handle failures, you reduce complexity, eliminate bug-prone error handling code, and create systems that automatically recover to known-good states.

The numbers speak for themselves. Nine nines of uptime. Millions of concurrent connections per server. Billion-user systems managed by small teams. These aren’t accidents—they’re the natural result of a philosophy that works with failure instead of against it.

For developers coming from traditional languages, this requires a fundamental mindset shift. You must overcome years of instinct telling you to catch every exception and handle every edge case. You must trust that sometimes the best error handling code is no error handling code at all.

Whether you adopt Elixir or not, the lessons apply: Isolate failures. Make recovery fast and automatic. Write simple code. And when something goes wrong in a way you didn’t anticipate, sometimes the smartest thing to do is admit defeat, crash cleanly, and start fresh.

Because in complex systems, a clean restart is often more reliable than limping along in an unknown state. The telecom switches, messaging apps, and distributed systems that keep our world connected have proven this. Maybe it’s time to let it crash.

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button