Erlang is far from perfect. The biggest limitation I’ve encountered is that there’s no neat way of doing a remote function call. While asynchronous programming is easier to scale, you sometimes need to send a message and wait for the result before you can proceed. Ideally, you would save the state related to the current message and proceed with processing the next one, but at the very least you should be able to wait for a specific message and then grab it from the message queue while still processing another message.
In Erlang, there’s no sensible way of doing this. You need to create a meta-message-loop that takes the messages from the message queue and then processes them in order, but allows them to be accessed out of order. This is something you might want to do often, which brings me to the second major limitation of Erlang.
There is only a very limited scope for meta-programming. While Lisp programmers look down on C for having very primitive macro support, even C programmers can look down on Erlang’s macro capability, which isn’t useful for much more than defining constants.
Overall, Erlang is a nice language. There are a few minor irritations, but the majority of the language is nice to work with. Anyone looking for a change of style, or a language for a large distributed project, would do well to download Erlang and play with it for a bit. The documentation is relatively good, and should be enough for a new user to get to know the system. Where it isn’t, the Erlang interpreter is very good for trying out things to check your understanding of how they work.