Having worked on fairly concurrent systems in Erlang specifically, I would say one of the most useful tools to understanding the behavior of the system is the tracing facilities. Even something as limited (but user friendly) as ErlyBerly[0] helps a lot.
As you scale up the number of concurrent processes who need to engage in ad-hoc concurrency, message passing becomes (in my experience) the only feasible way to manage those interactions. Being able to insert yourself in-between the communicating processes becomes necessary to truly understand what your system is doing.
This is true even outside of Erlang itself, such as deploying independent OS processes that use HTTP for communication. Wireshark has definitely been my friend.
State charts, interaction diagrams, and sequence diagrams are also invaluable.
As you scale up the number of concurrent processes who need to engage in ad-hoc concurrency, message passing becomes (in my experience) the only feasible way to manage those interactions. Being able to insert yourself in-between the communicating processes becomes necessary to truly understand what your system is doing.
This is true even outside of Erlang itself, such as deploying independent OS processes that use HTTP for communication. Wireshark has definitely been my friend.
State charts, interaction diagrams, and sequence diagrams are also invaluable.
[0]https://github.com/andytill/erlyberly