Adding on to sb8244's comments; writing sequential code is very nice, but it's not the gen_server behavior that gets you that. It's the lightweight processes.
gen_server is useful, but it's not much more than receive in a tail recursive loop and conventions around messages, most specifically about if the sender expects to receive a reply or not. It's not magic, and it's written in clear Erlang that anyone with a week of Erlang fiddling can understand (which kind of is magic; almost everything in OTP is clearly readable)
Concurrency comes from running many processes each with their own message queue.
gen_server is useful, but it's not much more than receive in a tail recursive loop and conventions around messages, most specifically about if the sender expects to receive a reply or not. It's not magic, and it's written in clear Erlang that anyone with a week of Erlang fiddling can understand (which kind of is magic; almost everything in OTP is clearly readable)
Concurrency comes from running many processes each with their own message queue.