I think if I were to relive my Erlang education, it would be to go against the severely pushed agenda of `no matter what, use behaviors from the get-go.’ gen_server is a great pattern, but it feels counter to everything functional programming and for that matter concurrent programming is all about. Sure, now knowing how the behaviors are implemented, it all fits, but for the first while, the behaviors felt magical (like OOP), stateful-feeling-ish (like OOP), and imperative (after all, 1. a gen_server is embodied within a single named process, despite that Erlang touts: go ahead, create millions of processes; 2. the most common thing to do with said servers is use =call= which is blocking/synchronous). In the end, of course, use the behaviors, but just blindly using them from day-one is a crappy lead-in to Erlang/OTP, and probably comes with many hidden costs as far as how fast & deep one learns the paradigms of FP and all things OTP.
I can’t agree more with this feeling. It is, in part, the inspiration behind this blog post. The idea is to first find the problems, try to solve them, figure out they’re always the same ones and somebody before you found them too, and only then using the tools that solve most of the problem for you and write your specific code.