FYI: behaviour is an API specification for modules, they don't have to run in their own processes.
However, it is the case that the most commonly used behaviours are gen_server, gen_statem, supervisor, etc, which do live in their own processes, and it is arguably most useful to use behaviours to determine the callbacks that an abstracted, general process-managing module uses to hook into the module you write that describes the implementation of a GenServer.
For an example of a non-process-bound use for a behaviour, one could have "ping" behaviour, which describes running an ICMP ping. You could then describe in abstracted form, the common points between an implementation that uses the linux standard ping, fping, a FFI call to low-level OS hooks and network stacks, a call to the new OTP 22 socket module. Or, in a test environment, write a module where it's backed by a stateful lookup table, where you can assume a ping response, but selectively turn off ping responses to IP addresses to simulate a failed ping response and verify downstream effects.
However, it is the case that the most commonly used behaviours are gen_server, gen_statem, supervisor, etc, which do live in their own processes, and it is arguably most useful to use behaviours to determine the callbacks that an abstracted, general process-managing module uses to hook into the module you write that describes the implementation of a GenServer.
For an example of a non-process-bound use for a behaviour, one could have "ping" behaviour, which describes running an ICMP ping. You could then describe in abstracted form, the common points between an implementation that uses the linux standard ping, fping, a FFI call to low-level OS hooks and network stacks, a call to the new OTP 22 socket module. Or, in a test environment, write a module where it's backed by a stateful lookup table, where you can assume a ping response, but selectively turn off ping responses to IP addresses to simulate a failed ping response and verify downstream effects.