9 months ago

Abstracted complexity remains complexity

Recently, I had to decide on a technology stack for Glossia, a localization tool I’m bootstrapping with my wife. Choosing a technology stack is an exciting decision for us, as software crafters. Still, it carries a profound impact on both the software and the business that builds on it, and should not be taken lightly.
​ Many factors can influence this decision, but simplicity weighed heavily in our choice. By controlling system complexity, we limit the resources needed to bring the tool into existence. This approach is key to bootstrapping the project without taking external investment, an option we’re not wholly against but prefer to defer.\

While assessing the complexity of various stacks, I noticed something revealing — many technologies that seem simple at first glance may either have complexity abstracted away, which can sneak through upper layers, or may present complexity relatively early, such as when you introduce a background jobs system.

Can we maintain a simple stack for as long as possible? Let’s look at some examples to understand better what I mean.

Ruby on Rails is a fantastic framework, and Ruby is a very idiomatic language that’s enjoyable to work with. Large organizations like GitHuband Shopify running their businesses on it speaks volumes about the technology. We could go a long distance with it building a product and attracting the first customers. However, you would eventually need a platform or developer productivity team to ensure that complexity remains under control, not impacting developer productivity. These teams might find themselves grappling with state mutation causing test flakiness, scaling the system horizontally using technologies like Kubernetes, or building sophisticated pipelines to debug production issues and heal the system swiftly. One might think this is normal in any software, but let me tell you, a technology that doesn’t require that does exist.

What if we choose JavaScript or TypeScript? They’re very popular and offer extensive ecosystems, allowing your app to leverage Cloud Infrastructure providers like CloudflareCool, right? Cool until it’s not. Pretty much any layer in the ecosystem is fragmented, which might foster creativity but isn’t practical for running a business. Reactcontinues to be trendy, but with the introduction of React Server Components and Vercel monopolizing its development, some are flagging it as evil and shifting towards web components. If your company opts for it? Well, good luck. Frameworks are abstracting an absurd amount of complexity and introducing a lot of indirection, creating the sensation that complexity doesn’t exist. But it does.It’s just hidden. When things don’t work as planned, you must wade through complex layers, not designed for navigation during issues, and spend days unraveling complexity rather than developing the actual product. If we could gauge the time spent dealing with these matters, my bet is that it’s substantial.

So what should I choose instead? I recently discovered Elixir and the Erlang VM, powerful technologies that can scale with low complexity, without compromising developer experience. They provide everything you need to build from a very basic to a highly sophisticated system, and that’s why I chose it for Glossia.

The world is concurrent, with things happening simultaneously, communicating via messages. Each of these occurrences, or processes, is an independent element with its memory and interface. Traditional programming languages propose a sequential mental model, which evolved with hardware enabling multiple simultaneous processes. However, issues like shared memory led to memory races and complicated concurrent software development.

Erlang, which preceded Elixir and provides the Erlang VM, examined the world and asked: why not model problems and solutions to align with real-world mental models? What if processes were easy to create, each with a portion of memory and CPU, communicating through messages? This fundamental idea prevents complexity that other languages naturally inherit, making software more logical.

Elixir runs on the same VM, BEAM, but offers a more modern language and toolset. It can handle millions of processes running tasks in parallel across cores and concurrently on each core. It does this fairly, meaning that if one process is busy indefinitely due to a bug, others can continue working. Thanks to this, we can scale the system vertically by adding more cores, usually a simple task, and avoid Kubernetes unless resembling a tech giant more than a small startup. Since the language is functional with processes to store state, code is more predictable, easier to test, and can even run tests in parallel with less likelihood of flakiness. How crazy is it that you don’t need all the additional efforts associated with other languages?

That’s not all. Elixir’s support for macros and compilation error checking, access to powerful tools like the observer, and its error tolerance make it an impressive choice. Phoenix leverages processes to support component-based UIs without the complexity of frameworks like React, easing real-time experience creation, which we may utilize in Glossia for a collaborative review functionality.

Elixir and the Erlang VM are fantastic, and I strongly believe they’ll help us stay focused on bootstrapping Glossia. I’m thrilled these days building with it, and everything is fitting together beautifully. The ecosystem is mature, and the community is welcoming.

About Pedro Piñera

I created XcodeProj and Tuist, and co-founded Tuist Cloud. My work is trusted by companies like Adidas, American Express, and Etsy. I enjoy building delightful tools for developers and open-source communities.