Why Erlang?
Twenty years ago, I wrote my first piece of Erlang. I was a kid, and hadn’t yet experienced the gripes and stresses of developing software to be used by other people. I loved to program and build things, but those things were for myself, under no deadline, on one machine.
Many years later, I started working as a developer. It took some time to get used to enterprise programming, and the various principles and practices it encompassed[0]. These ideas helped me grow as a programmer by diversifying my toolset and challenging my preconceptions of what it meant to build and maintain software over time. This piqued my interest as to what else was out there, and I spent countless of hours learning everything from Pascal to Haskell.
Eventually though, the dust settled. It became increasingly apparent that software development
is in fact not all about programming. On the contrary, it’s a multi-disciplinary job that includes programming. On top of that, it relies
heavily on successfully communicating and sharing ideas between many different people with different perspectives, ambitions and experiences.
Teamwork.
- Communication: Can we understand, trust and support each other?
- Onboarding: How do we ensure that everyone has the same goal and execution in mind?
- Training: Is everyone able to perform their task(s)? How do we help each other to get there?
- Version control: Can we control when and how the program changes over time?
- Testing: Does the software work? Just as it did last week? Last month? Last year?
If there is confidence, respect and trust within a team, accompanied by strong and consistent pipelines, the inevitible chaotic nature of software development can be contained and managed.
Any kink in that armor and this certainty gradually fades at an accelerating rate. The consequences of which can often be felt in legacy projects where (potentially breaking) changes are made ad hoc without much consideration, and where the general feeling is one of deafeated “who cares”-ism and “it can’t get much worse than this”. Left unchecked, this exacerbates with time until the system becomes a hot potatoe that no one wants to touch. There’s no credit in maintaining it, yet the consequences of breaking it could be disasterous to both the company and the person who broke it.
Therefore, when leads say "programming languages don't matter"
, this is what they mean; upholding and
steering a project over time is more important than choice of programming languages. A well maintained
project can easily outperform a lesser kept project with better technological choices.
Sorta.
Programming Languages Matter.
You’re about to create a basic CRUD server for storing configuration files (JSON, YAML), with a web frontend. This will be used internally, with a few hundred daily users. No single file is greater than one megabyte, total storage is less than one gigabyte. You have today to complete this task.
You’re given these language options:
- Ruby
- PHP
- C++
- x86 Assembly
Which one do you choose?
I’d argue that there’s a spectrum of valid and invalid choices here, where I’d be ok with the first two options, skeptic of the third and downright confused with the fourth.
Change the requirements to build a microcontroller for a medical device and the spectrum-of-ridiculousness for any option changes drastically. The language matters, deeply.
We're here to solve business problems.
And that means that we have to be cost effective and minimize risk.
Once we have identified our problem domain, consulted with customers and experts, our choice of technology is very important as whatever we pick now is what we’re going to be stuck with for the duration of our project[1].
Failing to choose correctly becomes apparent when you find yourself shoving triangles into square holes, yak shaving for problems you’re not having, or compensating for an insufficiently powerful programming language.
Coming from Lisps and Erlang, I remember things like installing dependency frameworks to pass arguments to functions, which have now become methods that might or might not have hidden state, taking a long time for me to internalize and accept as useful.
In a similar vein, not having access to those types of abstractions at a language level leads to a lot of duplicated efforts in almost-identical-but-not-quite implementations across projects, if not in the same project (despite attempts at reuse).
What followed were years of long discussions in and out of work, with anyone willing to be
bombarded with questions and ill-formed rants[2]. Hearing the opinions of developers
working on different levels of abstraction helped me realize that it’s not all cut and dry, and that "best practices"
are
very specific to what you’re doing, and aren’t always applicable even if you drink all the cool-aid.
What business problem does Erlang solve?
Erlang enables you to write scalable, concurrent, distributed and fault tolerant systems with soft-realtime latency guarantees (which in my experience describes most online systems out there[3][4]).
It does this by being built for solely this purpose, polished over the course of 35 years.
The language, the VM (BEAM) and the libraries (OTP) have been proven handling absurd
volumes of traffic in a range of different applications.
The cost of writing Erlang is that you have to approach most of your problems with (concurrent) message passing in mind. The benefit is that this becomes second nature rather quickly, and the line between one core and several-cores-on-several-machines becomes blurry after a while and you find yourself comfortable writing either as the abstraction thankfully never changes.
Erlang acheives this by taking Alan Kays definition[5] of "Object Oriented Programming"
to its fullest extent,
whilst firmly keeping a functional core
throughout the system:
- Idiomatically, modules run in isolated processes.
- Processes are tiny, and preemptively scheduled. Should one process hog the CPU, the rest of the system remains responsive.
- Processes communicate via message passing, there is no shared memory.
- If a process crashes, it crashes.
- Crashed processes are handled by Supervisors, which vary in behaviour, and may themselves be supervised. Most commonly, supervisors just restart the crashed process. This effectively prevents stateful Heisenbugs, as restarting a process is cheap.
- Functions pattern match on their inputs, which enables you to create the equivalent of overrides based on the values (and/or arity) of the parameters that you are receiving. It is the most useful language feature of any language I know/use, after first class functions.
- The language is small, and OTP is incredibly consistent in its interface. If you get part of it, the rest comes quickly.
So, if I had to pick one language/runtime to program server systems in, it would be Erlang[6].
It enables me to solve otherwise hard problems, where the majority of my time is spent on the
problem rather than the scaffolding, as it’s already there. There’s no non-exhaustive list of hot frameworks to learn, the
runtime is strong enough to do most of the lifting up front. Feeling that the language provides
just the abstractions that I need out of the box is something I haven’t found anywhere else,
and puts a smile on my face whenever I’m working with it. Nothing comes close.
Here’s hoping to another (perhaps more serious) twenty years with Erlang! Cheers!
Fredrik Holmqvist
[1] There’s always rewrites, but I’ve yet to see a rewrite that turned out more profitable than an equal amount of efforts spent improving existing implementation(s). On the contrary.
[2] I am very grateful to have met people who encouraged me to ask questions, and to gladly answer them. I’ve also had the luxury of sharing books with coworkers, and have had training on site on more than one occassion. These moments have helped me tremendeously.
[3] High throughput systems exist, but in my career the vast majority of business applications I’ve worked with operate on small amounts of data (megabytes) being passed around several endpoints that need to guarantee a steady and low latency. These systems aren’t allowed to go down. The medical domain is an example where the consequences of such an event would be dire.
[4] Of course, there are a tonne of other type of applications: Tools, native apps, number crunchers, games and so on. These fall out of the category for “scalable online systems”, as they are by (primarily) for one user on one machine.
[5] “OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things." ~ Alan Kay, link
[6] This extends to all languages on the BEAM, such as Elixir, LFE, Gleam, Clojerl etc.