1. The concurrency and distribution model. Processes (light weight green threads) and extremely cheap, isolated, and concurrent. Process can message each other, and messaging is location transparent. So you can send a message to another process on another Elixir server using the exact same primitives as sending a message to a process on the local server. This allows for all kinds of things that are hard or not reasonably possible in other platforms:
- Start a process on a node in us-east1, and message it from Tokyo. This is simply built-in.
- Run a primary DB in us-east1, and RPC from your Tokyo instances to perform writes. The RPC mechanism is again built in. You have code running on another node, and you simply run your code over there and get the result. There's no marshaling of data structures, deploying message queues, protocol buffers, etc
- Using Phoenix Pubsub, broadcast a message from anywhere in the cluster, `PubSub.broadcat(:my_pub, {:new_msg, "hi!})` and it will arrive to all instances who are subscribed, anywhere
This kind of stuff can be made to work well in Go and Rust, but you need to bring in libraries and do more work. They are absolutely great at "network programs", but lacking the distribution primitives means libraries and solutions are usually more bespoke vs Elixir where everyone in the community simply uses what is provided out of the box. So there is no interplay of ops or dependencies to try to reconcile.
2. Processes support stateful applications. Most of the LiveView-like solutions still go over stateless HTTP because websockets and cheap concurrency aren't as viable. This drastically limits what you can do efficiency wise. For example, our diffing engine requires state to live on the server so we know what changed. If you simulate LiveView over HTTP by sending all the state from the client for every interaction, you are sending way more data, doing all the HTTP authentication, fetching the world, then sending the entire template back.
3. Process are preemptively scheduled and load-balanced on IO and CPU. This allows your LiveViews to perform blocking CPU bound work and the scheduler will make sure every other user gets their fair time share. In other languages like Node where you rely on evented IO, any CPU bound work blocks the entire program.
4. Processes are isolated. On the evented IO example, imagine your websocket handler in Node.js has an uncaught exception caused by a single user interaction. It brings down the connections for all connected users. In Elixir, all processes are isolated, garbage collected in isolation, so you aren't jumping thru hoops to handle these kinds of degradation modes.
Hope that helps!