There’s a famous article by Peter Norvig called Teach Yourself Programming in Ten Years. In it, Mr. Norvig intends to discourage programming language learners from using resources that promise quick roads to mastery. He advocates instead for a long period of practice that can be counted in years rather than days or weeks.
I agree with him. What I want to talk about today, however, is: How much time do you need to become a productive contributor to a project written in a language that you have to learn from scratch? I claim that this time, given a well-designed language and a supportive company culture, can be way shorter.
I’ve used Elixir for a bit over six months. In this write-up, I want to tell you about my experience and share the resources that have helped me along the way.
To do that, I’ll first say a couple of words on my pre-Elixir experience.
Once that is settled, I’ll say something about how I approached existing Elixir projects in my job, how I supplemented my practice with more theoretical material, and how the community helped me learn.
Finally, I’ll relate my experience with my first bigger project, how I started reflecting on the missing pieces, and how a company with a learning and teaching culture can help you grow as an Elixir engineer.
Where I Come From
I started my career in the industry writing code in C++. That setup accompanied me for the best part of five years, so not enough (for me) to really grasp it but plenty to be humbled by its complexity.
My main pain point was that whenever I wanted to accomplish something useful via programming, C++ was never the right tool for the job, the reason being that it’s too low level to ever arrive at something tangible, even less when you want the UI to be a Web page.
Using C++ also means I’ve always only used imperative programming and I used to be a hard-core advocate of object-oriented programming.
I moved to Web development after five years of an exclusive OOP diet. What a shock! That was also my first ever experience with a language that allowed closures (you can do something similar in C++, except functions there are not first-class citizens).
A couple of years later I started using Elixir.
My first assignment was a bug to fix in a complex (hasn’t that become redundant these days?) system written in Elixir. My company has a strong penchant for functional programming, and that is reflected in all projects including those where the primary language is not Elixir. I think this is important because it is easier to adopt a language like Elixir if your workplace doesn’t have prejudices against functional programming.
But in other languages you have a choice. In Ruby, for instance (pun intended), classes and OO — they’re all still there. Elixir frees you from those abstractions and gently pushes you to think without them. In a way, it’s a simplification that feels liberating.
Pairing with a colleague, I started to get a grasp of what was going on. Pipes? It might take a while but they sink in. The experience is probably similar to getting used to parentheses in LISP. An example:
Once you grasp the idea of a pipeline of actions, it feels very readable (in this case, we’re flattening a list — notice that
is a list itself inside a list — and then doubling each element).
Recursion and the absence of loops might take a bit longer but starts feeling natural pretty quickly too. I was accustomed to using
for-loops but in time managed to practice enough to make
reduce feel natural.
And it works. Elixir encourages you to build many simple blocks which can be thought of in isolation, because Elixir functions are pure, and as such allow you to ignore the state of the rest of the system. More articulate language advocates than me have said it better, but thinking of things in isolation helps you understand concepts more clearly and express them in code, and leads you to a stronger confidence in the way they interact. What is more, Elixir is so expressive that relatively simple projects achieve substantial stuff (another way to say that the language is expressive).
At that point, I wanted to improve my knowledge of the language through some theory. Here’s another great reason to adopt Elixir: it sports a very friendly community and high-quality documentation.
The resource I found most beneficial was the one that many of my colleagues recommended to me: the course Developing with Elixir/OTP from the Pragmatic Studio. The clarity of the course helps considerably in putting the pieces together to offer a broader appreciation of what is going on in the BEAM and how that helps developing reliable systems.
But theory without practice is not very useful (a concept stated also in the course I just mentioned). Besides going through the engaging exercises provided in the course itself, I made use of the mentor community at Exercism, which provides feedback on the problems solved and helped me understand a few mechanisms of the language (like the difference between strings and charlists). The Elixir Forum is also an excellent source of information and it pops up quite often when you google anything Elixir-related. Finally, the quite useful Elixir Slack workspace drives together a community of engineers where more than once I’ve asked novice questions and got almost instant support.
I also decided to dive into some more organized resources. I picked up Real-Time Phoenix which is an excellent book but already at a level way more advanced than I was at the time (or am now). That was an invaluable experience nonetheless, since I shared my reading progress with a group of other engineers in the company in weekly meetings.
Finally, the spaced-repetition software Anki has helped me retain many of the concepts I saw for the first time¹.
More Practice: First Project
At this point my team started a new project with Elixir. That has been a great experience and has helped a lot in solidifying the concepts I had acquired along the journey. There was still a lot of hand-holding, but it was an eye-opener on what Phoenix is capable of. That also spurred me into developing my first Plug (more on that in the next article).
What I found impressive was how a bunch of people, all relatively inexperienced with the language, could put together a solid solution almost from scratch in a relatively short time. (We don’t seem to be the only ones in this position, as for instance the Discord team also started using Elixir and learning it on the job).
The project’s aim was to develop a platform that allows creating rules that describe events in a Web application that can trigger actions under certain conditions. We had a similar project written in Ruby, and that was surely one less element of surprise that allowed us to concentrate more on implementation and experimentation and a bit less on design. We also had other projects in Elixir within the company, which was useful because we spent less time figuring out the configuration parts.
Three months or so of work and we got it done successfully. It’s now been in use in production for a while and I must say it is probably one of the projects with the least amount of issues I’ve ever worked with.
I recognize that a lot of the heavy lifting is already in place. As mentioned earlier, the BEAM provides an incredible resource for reliable network communication. But the logic of the application was very natural to map against Elixir concepts, and this sped up the development and reduced the errors considerably. I have no numbers to back this statement, but I do have a few projects under my belt, and this experience has been one of the smoothest so far in my career.
Allow me an aside. I’d like to stress a point on sheer practice. When it comes to understanding the language itself and what it can offer, I think that I got most of the value from the Advent of Code 2020 challenge. There is something in solving problems in isolation that lets you appreciate aspects of the language that you don’t normally pay attention to when working on bigger projects, helped by expressive libraries and supported by frameworks like Phoenix. I think that’s where many of the tiny details of, e.g., working with maps and structs sank in for me.
By the time the project entered maintenance mode, I started having a better feeling of what pieces I was missing. I’ve read that I’m not the only one to work with Elixir for a few months without ever having to really understand what a GenServer is for.
Phoenix and Ecto are still mysterious beasts to me, and so is testing with Mox (even though I’ve written plenty of tests for the project, it’d be hard to remember the syntax — just not enough practice).
Not to mention metaprogramming and macros, a topic that in my C++ days was a source of fears but that seems to be somehow friendlier in Elixir. It also is one of the powerful aspects of functional programming. Here’s the good news: it’s good to know it, but it’s not good to use it as it makes your programs harder to read. (This was mostly accepted also in the C++ community.)
All the resources in the world become of lesser help if you can’t experiment at work. I am lucky to work in a company with a culture that encourages learning and teaching. We have been deliberate about putting together a group of people with a will to learn, and even better, a will to teach each other. I must say this is the best kind of support material you need to master any technology.
Conclusion: What It Takes to Learn a Language
Among the ideas in that article by Peter Norvig I opened with, one is to learn languages from different paradigms to get a feeling of what each does differently and how that is a strength or a liability depending on the situation.
Elixir is an excellent gym for thinking functionally. It makes it enjoyable because the community around it is helpful and inclusive, and the technology itself is designed well enough to allow you to grasp it to the point of becoming productive very quickly.
What that article of Norvig doesn’t mention explicitly is that you need a company culture that allows that. Come work with us. You’re still putting in the sweat, but we are giving you all the support to make it worth it.
- Spaced repetition has probably narrower application in Engineering than, say, when learning (spoken) languages. But some concepts are worth remembering so that you can pull them out when needed, without having to research them in a browser. One example of how I used it in Elixir is to learn concepts from the book Real-Time Phoenix. Say you want to know and remember what are the responsibilities of a Channel client. I’d create a list of them and use cloze deletion to remove one bullet from the list each time I’m shown it, and try to remember the missing one.