You may not need GenServers and Supervision Trees

The thought that people seem to think of GenServers and supervision trees in the elixir/erlang world as too essential deterring people from using these languages has been brewing in my mind for quite some time. In fact I have written about it before in Elixir Forum. This is a summary and extended version of that (including some choice replies) to give it more visibility.

It feels like we talk so much about GenServers etc. that people who come to Elixir feel like they need to use them or they are not “really” using Elixir. I fear that it keeps people out of the elixir community because all this just seems so complicated. However, you can write great applications without ever writing them yourself. I emphasize yourself because a lot of the libraries you’ll use (phoenix, ecto etc.) already have their supervision trees and their processes – you’re standing on the shoulders of giants truly. Still,  I hear people say something to the tune of “We’re still using this like Rails – we should use GenServers” – without any need or concrete reasoning. Having something as simple as rails (in parts, let’s not derail about rails here) but at the same time fully parallel and more efficient is a huge boon to my mind.

I feel like hardly anyone ever highlights this. I’ve been programming elixir since ~2015. I’ve never written a production GenServer or supervision tree (and I have multiple applications in production and multiple published libraries). I simply didn’t encounter these problems or the eco system took care of it for me. Still I felt somewhat inadequate because of it, as everyone seem to already be talking about these. Which is also understandable, they are unique, they solve hard problems – there’s lots to learn and share knowledge.

I’ve had this thought that often you don’t really need GenServers and supervision trees in my head for a long time (~2016) but was too afraid to voice it. Maybe I just don’t understand it enough? Maybe I need to learn more about them? Everyone always talks about them, I’m sure enlightenment will come to me some time soon! I finally nervously wrote the aforementioned Elixir Forum thread in 2018. My nervousness went down a notch after José Valim, creator of elixir somewhat hyper productive and omnipresent, liked the post within ~15 minutes of its creation. Also most people were really supportive, hence I’m happy to share this more openly, sorry for the delay a lot had been going on in my life 😉

Be mindful that I don’t say you don’t need them – I’m merely saying that you can build significant and great elixir applications without writing your own GenServers and supervision trees. It’s of course still a topic worth learning.

GenServers? Supervision Trees? Processes?

If you’ve ever watched a talk about elixir or erlang you’re likely to have heard about these. They’re one of the “killer features” of erlang and elixr. They give you that famed parallelism backed by the actor model, reliability and that whole “Let it crash! (and restart in a known good state)”. Both GenServer and Supervisor are behaviours showing a process “how to behave” and they both ship with Erlang/OTP.

So what’s the problem with Genservers and Supervision Trees?

GenServers, supervisors etc. are great technologies that help you solve problems. They’re one of the things that is most special & unique about elixir & erlang. As a result lots of conference talks, blog posts etc. focus on them and it seems everyone wants to use them. Through the big focus on them in the community it sometimes feels like you can’t be a “real” elixir/erlang programmer until you’ve used and mastered them.

However, do you need them all the time? At least while using a framework (like Phoenix), chances are you don’t. The hidden detail of course is that you are using GenServers and friends without even knowing it – Phoenix runs every request and every channel[1] in their own processes. Ecto has its own pool for your database connections. It’s already parallelized and you don’t need to take care of it. That’s the beauty of it. What I’m saying is that in the standard situation the eco system takes care of you.

Building a relatively standard CRUD web application with Phoenix? No need.
Just using channels for a chat like applications in Phoenix? You’re good.

Of course, you don’t need them until you really do. I like how Jordan put it:

I agree that you, 99% of the time you do not need to use anything otp related but when you are in that 1% its really a game changer that makes elixir as great as it is.

The Bad and the Ugly

It’s not like using GenServers and friends makes everything instantly better. Quite the opposite, it can make your code both harder to understand and slower.

Sometimes the introduction of processes just complicates the code, what could have been a simple interaction is now obfuscated through a bunch of GenServer calls. Through trying to use these concepts you can also essentially grind your application to a halt performance wise. To take an example from the excellent Adopting Elixir, which also covers this topic:

A new developer team started building their Phoenix applications.
They had always heard GenServers could be treated like microservices but even
tinier. This “wisdom” led them to push all of their database access control to
GenServers .
(…)
performance was abysmal. Under high-enough load, some pages took 3 seconds to render because they built a bottleneck where none existed. They
defeated Ecto connection pools because all access happened through a single
process.
In essence, they made it easy to create global, mutable variables in Elixir. They
essentially crippled the single biggest advantage of functional languages, for
no gain whatsoever.

When to GenServer?

Adopting Elixir again provides some guidance as to what to best use processes for:

  • Model state accessed by multiple processes.
  • Run multiple tasks concurrently.
  • Gracefully handle clean startup and exit concerns.
  • Communicate between servers.

They especially highlight that GenServers aren’t to be used for code organization.

Robert Virding (one of the creators of Erlang) also chimed in and his response is so measured that I want to quote it in full:

I think the most important thing to understand and to use properly is the concurrency in the problem/solution/system. Using GenServers and other behaviours is just one way of doing the concurrency but it is not the only way. They are tools and like all tools they need to be used in the right way. The problem is to get the right level of concurrency which suites your problem and your solution to that problem. Too much concurrency means you will be doing excess work to no real gain, and too little concurrency means you will be making your system too sequential.

Now, as has been pointed out, many packages like Phoenix already provide a pretty decent level of concurrency which is suitable for many types of applications, at least the ones they were intended for. They will do this automatically so you don’t have to think about it in most cases, but it is still there. Understanding that is necessary so you can work out how much concurrency you need to explicitly add if any. Unfortunately because it is all managed for you “invisibly underneath” many don’t realise that is it there.

While people agree in general, there are also some that say that most systems can benefit from a GenServer and supervision tree. As Saša Jurić points out:

With a lot of hand waving, I’d say that GenServers are OTPs built-in building block for building responsive services, Tasks are the same for non-responsive ones, and supervision tree is the built-in service manager like systemd or upstart. In the past 10+ years of my backend side experience, I’ve worked on small to medium systems, and all of them needed all of these technical approaches.

He also has a fairly extensive blog post on the topic of when to reach for these tools and when not to: To spawn, or not to spawn.

In the end, I’m also not an expert on GenServers and Supervision Trees – as I said I never wrote a production one. Still learning and still growing. I think knowing them well gives you a good basis to make informed decisions on when to use them and when not to.

Abstractions

But you came to Elixir for the parallelism! So you need GenServers right? No.

Elixir core and the community have been very good at providing easy to use solutions to write fully parallel programs without having to write your own GenServers and supervision trees. There are gen_stage, flow and broadway, but somewhat more importantly a couple of these are built-ins like Task (do something in parallel easily) and Agent (share state through a process).

Want to geocode the pick up and drop off addresses of a shipment in parallel and then wait until both have finished? Say no more:

Learning

Although I’m saying you don’t need it and can write applications without it just fine, it’s a fascinating and interesting topic that can make you a better programmer without ever writing your own supervision trees even. And as I said, education is key to know when to reach for them and when not to. So here are a couple of books I can recommend to up your knowledge:

  • The Little Elixir & OTP Guide Book – my personal favorite, in it the author takes you through writing implementations of poolboy starting with a simple one, showing what shortcomings it has and then extending on it building a complicated supervision tree but you get the reasoning to go along with it to see for what feature what level of complexity is needed. A fascinating read for me.
  • Adopting Elixir – covers many aspects as mentioned before but is especially good at what to use these concepts for and what not to use them for (and is an interesting read overall if you consider to get your company into elixir)
  • Elixir in Action – Saša Jurić is one of the most knowledgeable people I can think of about this topic, hence I quoted him before, and a significant part of the book is dedicated to it too. A second edition came out earlier this year so it’s also all up to date.
  • Functional Web Development with Elixir, OTP, and Phoenix – build a simple game, first with simple functions, then develop a GenServer interface to it with some supervisors and then wire it all up in a Phoenix App – good introduction and especially good at showing some solid debugging work.
  • Programming Phoenix – introductory book I wish everyone writing a phoenix application read first, as it also covers why things are done in a certain way and it’s by the authors of the framework which gives a unique perspective. It also has the aforementioned information of what is already parallelized etc. It also includes a pretty cool use case for a supervised GenServer (getting suggestions from an external service and ranking them).
  • Designing Elixir Systems with OTP – this is shaping up to be a great resource on GenServers, Supervision Trees and OTP in general. I haven’t read it yet, but James, Bruce and PragProg have all my trust plus I read some early praise already.

Final Thoughts

Well, you don’t need GenServers and supervision trees to start with writing elixir applications! Go out there, write an application, play with it, have fun, call yourself an elixir programmer (because you are!). Still, learn about OTP to expand your mind and to know where to look when you encounter a problem where a supervision tree could help you.

When discussing this in Elixir Forum, Dimitar also came up with a good thought: Maybe the OTP is what pulls people in and helps them discover all those other nice things?

I came for OTP. I stayed for the functional programming.

As a community, I think we should make it clearer that you don’t have to use GenServers and that doing so might actually be harmful. Of course all those conference talks about how to use them, distributed systems etc. are very cool but every now and then give me a talk about how a business succeeded by writing a fairly standard Phoenix application. Don’t over complicate things.

I’m not saying you shouldn’t learn about GenServers. You should. But know when to use them and when not to.

Lastly, if you disagree I want you to scream at me and teach me the error of my ways :smiley:

 

[1]Technically the web server of phoenix cowboy doesn’t use GenServers and supervision trees for normal http request handling but their own thing, they have a similar functionality though so it still holds true that you don’t need to roll your own. Thanks to Hubert for pointing that out.

edit1: Correctly mention new edition of Elixir in Action, be more specific about Cowboy Processes and include a thought in closing paragraph. Thanks go to Saša, Hubert and Dimitar.

ThoughtWorks Boot Camp

I just spent the weekend in Hamburg at a ThoughtWorks boot camp. So this blog post is for people who want to know what a boot camp at ThoughtWorks is like. Also there is information about ThoughtWorks in this post but if you don’t already know them you should check out their homepage.
Oh on a little side note, since I don’t know whether or not everybody would agree to this I won’t mention names in this post, although this post is solely positive.

Personally it will still take me some time (~ 2 years) to finish my studies, but I’d love an internship at ThoughtWorks and wanted to get into touch with them as soon as possible, that’s why I took the voyage from Sweden to Hamburg.

ThoughtWorks is not your average company. So the hiring process is not average as well, at least not what I’d expect as average – it was my first “real” job interview. And I felt really comfortable but continue reading if you want to know more…

Day 1

The boot camp started off at 8:30 on Saturday with a nice breakfast and a “meet and greet” with some ThoughtWorkers and fellow applicans. The dress code was casual, no one wore a suit. Most people were wearing jeans and a t-shirt or/and a pullover. Everybody was really nice and open. There were ThoughtWorkers present you could just go ahead and talk to them.

One of the key moments for me on the first day was when one of the ThoughtWorkers wanted to tell something about the company in general and suggested that we all just sit down on the floor. I guess this wouldn’t happy in many companies, everybody would sit down in a big meeting room which would give it a real formal flair. It was a cool informal session, we could ask whatever question we wanted and it had a real friendly atmosphere. The atmosphere of a company I want to work in. Some of the key takeaways were that ThoughtWorks has a really flat hierarchy and that you can go to almost everybody and talk to him/her. Moreover ThoughtWorkers travel a lot, to wherever the client is. Therefore it was the first time for many of them that they saw the German office, despite working in the German office for quite some time. Also ThoughtWorkers seem to meet up quite friendly, when they are in the same town in order to have a beer (or 2).

It was emphasized, that we are not competing with each other for a job. ThoughtWorks is looking for talent and will hire the people that they feel fit the company. This philosophy was made apparent when one of the employees told his story: He is Canadian, but they didn’t have a job for him in Canada but wanted to hire him anyway. So they asked him if he would want to work in Australia and he agreed. Since then he has also worked in India and Germany (during the course of 2 years).
All of this resulted in a kind atmosphere in between us boot camp attendees, I got to know some pretty nice people.

After that a coding exercise started, it was a pretty basic task involving the modeling of some real world objects. It was said that we should especially pay attention to 4 things:

  • clean code
  •  good object oriented design
  •  best practices
  •  tests (preferably Test Driven Development)

We had 4 and a half hours time to develop the little application. Two of the developers were our customers we could ask whenever we had a question about the specification. Also we could just chat with each other or some ThoughtWorkers.
When I ran into Ruby problems I’ve never run into before (more on that in a later blog post) another boot camp attendee, that was also doing Ruby, came over and paired up with me to solve the problems since he already solved the problem. Thanks again!

In the end I wasn’t content with my code since so much time passed chasing those weird bugs. But it was still solid and the program did what it was supposed to do. And I learned a lot so those bugs won’t bug me again.

After that we had lunch – ThoughtWorks ordered some pretty tasty pizza. Oh speaking of food and drinks, we could just go to the fridge and grab whatever we wanted and during the coding exercise they would come around and hand us sweets.

After that big break with some good conversations it was time for logic tests. I don’t want to spoil it too much but the first test was difficult because there were 50 questions and just 12 minutes time. The second test was hard because it demanded high concentration and good analytical/algorithmic thinking. I liked the second test pretty much 🙂

After those tests it was again time for interesting conversations and a nice wrap up where everybody said one sentence about what the day was like for him/her. I stayed a little longer after everything was officially over (concerning day 1), since I enjoyed the conversations. And indeed I had a nice long conversation with a ThoughtWorker about… just about everything: what working at ThoughtWorks is like, what I do, what he does, code katas, TDD, whether Berlin or Hamburg is the better city and many more things.

Around 20:00 you’d get a call whether or not you made it to day 2 and luckily I made it.

Day 2

On day 2 the devs had 3 interviews. Well all the devs but me, since my studies still take me some time and therefore it wasn’t that urgent. I just had the cultural interview, which was basically about what I’m like as a person, what I do in my free time, what I’ve already done but also what it is like to work at ThoughtWorks and my expectations about a job at ThoughtWorks. So basically, nice chatting mixed with questions like “What would you do if…”.

The others had a management interview and a technical/pairing interview where they talked about technical things and played a bit with the code, that they wrote yesterday. Since I was especially interested in the latter (feedback on what I’ve done is always good) I approached the developers doing this and asked if they would be so kind and willing to give me a little review of my code since I didn’t get that interview. And luckily they agreed and we had a nice look at my code, making it a lot more beautiful, again after everything was officially over. But fortunately they seemed to like it overall 🙂 Thanks again at this point for taking their free time to give me feedback.

Wrap up

I liked it, especially the more or less little things. You could approach every ThoughtWorker there and have at least a little chat with them. I could call everyone by their first names (which is especially atypical in Germany). And they all seemed to be enjoying to be there, meeting potential new ThoughtWorkers. I think I have at least had a little chat with every ThoughtWorker present. One ThoughtWorker even paid for my lunch on Sunday (thanks!). We got thanked so many times for attending the boot camp. It felt great.
I’d describe the atmosphere as almost familial as I could always talk to everybody. When I had weird problems with my code I just asked a ThoughtWorker if he’d mind taking a look at it since my tests were behaving weird and I simply couldn’t find the reason. He came over and together we tried to figure out the problem, unluckily unsuccessfully on our first try but it was a good time anyway.

My wish to join ThoughtWorks grew during this weekend as I really felt comfortable there. I sure hope an internship works out, that I can work there when my studies are finished and that I get a T-Shirt*.

So all in all I can only recommend attending a ThoguhtWorks boot camp to everybody interested in Agile and Software Engineering. It’s a really cool atmosphere and I sure learned a lot. To me it felt more like a group of smart people that wanted to get to know people and choose whom they would want to work with in the future.

*I’m kind of a T-Shirt nerd. I got T-Shirts of nearly everything I like. Starting with bands, but also programming languages, operating systems and even my favorite browser.