left arrow Back to posts

What makes Elixir great for startups?

Anthony Accomazzo
@accomazzo
8 min read

Love Elixir, Postgres, and open source? We're building Sequin, a Postgres CDC tool to streams and queues like Kafka, SQS, HTTP endpoints, and more. Join us – we're hiring Elixir engineers in San Francisco.

The biggest problem most startups face is trying to find product/market fit. The key to discovering product/market fit is rapid iteration. You need to build, measure, and adapt quickly based on customer feedback.

Ruby on Rails understood this well, and bootstrapped tons of really big companies. The top four YC companies by value are Airbnb, Stripe, Coinbase, and DoorDash – all originally Rails shops.

Rails was and is really good. It's surprising that other languages and frameworks still haven't caught up. I feel for younger startup teams that don't know how good we had it.

There's one ecosystem that doesn't have a Rails-shaped hole, though: Elixir.

Elixir is a great choice for a team that wants to move fast. As I hope to demonstrate, Elixir – and its web framework Phoenix – provide an amazing platform for writing and iterating applications quickly. And with superior concurrency primitives, Elixir/Phoenix takes the Rails ethos to new domains, from infrastructure, to data pipelines, to CRUD.

Phoenix/Ecto

Ruby on Rails had it right: you want batteries included. The web stack is the last place a startup should be innovating.

Phoenix was built by an ex-Rails team, and they took all the best parts and left out all the bad parts. It delivers the productivity benefits of a full-featured web framework while maintaining transparency and control.

Rails is often criticized for being too "magical" – Phoenix doesn't receive this criticism. Phoenix applications are easy to understand.

A great web framework comes bundled with a great ORM, and Elixir's – Ecto – delivers.

You don't need to bring your own migrator tool. If you want auth, you can run mix phx.gen.auth and get great, well-vetted authentication code vendored right into your project.

These basics should be free and consume none of your startup's time.

The standard library

Elixir ships with a fantastic standard library that makes development both pleasant and productive.

In particular, Elixir's Enum and Map modules are complete and well-designed, making it easy to write idiomatic functional/immutable code.

Monoliths

Under few circumstances should a pre-product/market fit startup be writing microservices. Distributed systems are very challenging, and your startup has enough problems.

Teams typically adopt microservices for a few reasons:

  1. Independent scaling
  2. Team autonomy
  3. Fault isolation
  4. Incremental deployment

If you're an early-stage startup, that whittles the list down to two concerns: (1) independent scaling and (3) fault isolation.

It's easy to write monolithic Elixir apps. And it's easy to isolate runtimes while doing so. So a single monolithic Elixir app definitely gets you (3) fault isolation, and gives you some manner of (1) independent scaling.

Remote shells

The REPL is an amazing programming tool. What makes it even more powerful is when you can connect it to a running instance of your application.

Being able to run a REPL connected to your node is a boon for development. It's hard for me to imagine debugging without that power now.

During development, you can:

  • Boot your application and test functions interactively
  • Inspect state and debug issues in real-time
  • Hot reload code changes without restarting

But the real magic happens with production access. With proper safeguards, connecting a remote shell to production environments can dramatically accelerate your team's ability to respond and adapt:

  1. Fewer internal tools needed: Instead of building dashboards for every administrative function, you can leverage CLI commands through the shell.
  2. Debugging: When an issue occurs, you can inspect the live system, examine process states, and diagnose problems without extensive logging or needing to redeploy.
  3. Hot code reloading: You can apply targeted fixes without service disruption or having to wait for CI/CD to rebuild and deploy a new Docker image from scratch.

Obviously, great power/great responsibility. But we find that with some process around remote shell, you can mitigate the downside risks, while maximizing the speed gains.

Toolchain

The Elixir toolchain – like its dependency manager mix – is so good, that I never have to think about it.

At the moment, Elixir's weakest toolchain link has historically been its LSP story. But that's actively being addressed: multiple LSP projects are currently being merged into one "official" one.

The BEAM and OTP/concurrency

The crown jewel of Elixir is the BEAM virtual machine it runs on and Erlang's Open Telecom Platform (OTP) libraries. Together, they provide both a runtime and concurrency tools that are simply exceptional.

Erlang/OTP got a lot right. The platform is one of the most seriously underrated pieces of software. You see how low-level design decisions and guarantees – like the BEAM's "everything is a process" architecture – allow you to build up layer after layer of powerful abstractions. All the while making every layer predictable and ergonomic and unsurprising, because it has the same design, feel, and laws of physics of the previous layer.

If you're building a CRUD app, you probably won't use a lot of this stuff directly. You still benefit, though, as everything you use – from Phoenix controllers to Postgres queries – use this plumbing. This means your application automatically inherits the reliability, fault tolerance, and scalability benefits of the BEAM, even if you're not writing concurrent code yourself.

If you're building something like Sequin, you'll face concurrency and distributed system challenges. And you'll feel spoiled having these tools ship with the language. Everything from message passing, to shared buffers, to concurrent collection iteration is all built-in.

OTP makes it easy to build really fast pipelines that can handle a ton of throughput by leveraging all of a machine's cores efficiently.

Small components

Because Elixir is so expressive, and the standard library/OTP is so complete, I find that Elixir components just don't get that big.

You'll see this when you peruse Elixir libraries. Elixir libraries tend to be pretty small – you can do a lot with a little. So, this means they're easy to read and understand.

LiveView/LiveSvelte is the fastest frontend stack out there

As we've previously written about, we use a combination of LiveView and LiveSvelte. I think it's the fastest frontend stack to iterate with out there.

The industry has converged around SPAs, with React frontend and API backends. This stack requires a lot of boilerplate. And introduces a surprising number of distributed systems challenges. Further, a ton of business logic ends up moving to JavaScript/the frontend.

In our stack, almost all our frontend business logic is in Elixir. And it's on the backend, in the trusted environment of the server. We can easily keep our JS/HTML code very simple and dumb – Svelte in most places is a templating engine.

This means it's easy for one developer to quickly ship full-stack features.

Data plane or control plane

At Sequin, we have a "data plane" which is the pipeline from Postgres replication slots to destination sinks. And a "control plane" which is the CRUD application that allows you to create, observe, and manage these sinks.

Elixir is great at both of these. Elixir is a first-class platform for data pipelines, owing to both its bones (OTP) and specific data pipeline libraries like GenStage and Broadway. And, as I've mentioned, its CRUD capabilities are first-rate.

The LLMs write great Elixir/Phoenix code

As LLMs began seeping into programming, I was concerned that LLMs would only be good at a few select languages. And that, as a result, the industry would converge.

It seems the opposite has actually happened. Claude Sonnet crushes Elixir. I can't remember the last time it made a syntax error. Its Elixir is surprisingly idiomatic. The whole standard library is in the training set. Sometimes I'll need to feed it docs on bits of OTP, but it seems to have a great handle on all the tools in the Erlang toolbag too.

It's evident that LLMs are very good with syntax, and can fluidly transform an algorithm in one language to an algorithm in another. It also makes me wonder if Elixir actually benefits from being more niche: there's less Elixir code out there, but the Elixir code that is out there tends to be of above-average quality.

What are the drawbacks?

Finding developers that know Elixir

Far and away, the biggest drawback for a startup is that it's hard to find developers with prior Elixir experience.

We're hiring in-person in San Francisco, which adds another layer of difficulty: Elixir is a global language, and there's little regional concentration in San Francisco.

So, we've accepted it will be a mix of hiring Elixir experts and training non-Elixir developers up. We think this will be just fine: it's a stack you can learn quickly. And many developers are curious about functional and immutable languages and what OTP has to offer.

Non-concerns

Will I need to learn Erlang too?

No. The only time you're looking at Erlang is when you're reading the OTP docs. So, you're just looking at function signatures. And Elixir has the same data structures/types as Erlang.

Even less so with LLMs. If you're so inclined, you can just point the LLM to the OTP doc you're interested in and ask it how to use a function in Elixir.

Is deployment hard?

No harder than any other language. We use the standard Dockerfile available on Elixir's website.

If you want to cluster Elixir nodes, that will require a bit more ops work. But there's a lot of ways to do it.

If the ecosystem is small, aren't libraries limited?

This concerned me going in, but it really hasn't been an issue; the ecosystem has what we need. And because components are small, if an ecosystem tool doesn't quite fit our requirements, it's really easy to extend it so that it does.

In my several years using Elixir, I only recently hit a problem here: Elixir's Redis Client doesn't support Redis Cluster. However, we were able to instead use an Erlang library for this (backed by Ericsson no less), so it ended up being fine.

What about compile times?

From what I understand, Elixir/Erlang have made huge strides here in the last several years. I don't feel qualified to answer, as Sequin only has ~34k lines of Elixir (non-test) code at the time of writing. And compilation speed is not a problem at all for us.

But if your startup is able to scale to the point that this becomes an issue, I'd chalk it up as a good problem to have?

Conclusion

Startups need technologies that enable rapid iteration while maintaining a path to scalability. Elixir delivers on both fronts in ways few other technologies can match.

With Phoenix, you get Rails-like productivity, but with a functional/immutable language that has best-in-class concurrency primitives.

And it feels like the best days are ahead.

On the Phoenix side, LiveView just hit 1.0, and projects like LiveSvelte are pretty new. I think there's room for the most productive frontend stack to get even more productive.

On the language side, Elixir is getting types! Elixir is rolling out a compelling design for a gradual type system. The inference work is almost complete, meaning Elixir's compiler is able to infer types in Elixir programs without you needing to write any type annotations. Type annotations are next up.

Gradual type systems are perfect for startups, as you can introduce type guarantees as needed as parts of your codebase harden.

Elixir is a total joy to work with. And thanks to Phoenix, it's hard for me to imagine going zero-to-one with anything else.