Why the BEAM? — mob v0.7.20
Search documentation of mob
Default
Default<br>In-browser search
Settings
Why the BEAM?
Copy Markdown
View Source
Your app is a distributed system whether you like it or not<br>A mobile app talks to a server, handles push notifications while in the<br>background, manages local state, streams data, processes user input — all<br>concurrently, all the time. Most frameworks pretend this isn't true and make you<br>assemble it from callbacks, promises, state machines, and background workers.<br>You spend more time wiring concurrency plumbing than building your app.<br>The BEAM was designed in 1986 for telephone exchanges — systems that handle<br>millions of concurrent connections, never go down, and update themselves while<br>running. It didn't get these properties by accident. They are the entire point.<br>When you run the BEAM on a phone, you get all of it.<br>What that actually means<br>Concurrency that doesn't hurt. Every screen is a GenServer. Every<br>background task is a supervised process. You don't choose between async/await<br>patterns and state machines — you just write functions that send messages.<br>Ten thousand concurrent processes on a phone costs less than one thread in most<br>runtimes.<br>Fault isolation by default. A crash in one screen cannot corrupt another.<br>The supervisor restarts it. You don't write defensive code everywhere — you let<br>things crash and write recovery logic once, at the top of the tree.<br>Hot code loading. Push new BEAM files to a running app and the code changes<br>in place — no restart, no lost state, no user impact. This works in development<br>(see mix mob.deploy) and it works in production via OTA update. No App Store<br>review required for Elixir changes.<br>Distribution is a first-class primitive. This is the one that changes what<br>apps are possible.<br>Mob.Cluster: phones as nodes<br>In the BEAM, every running instance is a node. Nodes connect to each other<br>over Erlang distribution and immediately share the full OTP primitive set:<br>remote procedure calls, message passing to a pid on another machine, distributed<br>process registries, global GenServers.<br>Two Mob apps that share a cookie become a cluster:<br>Mob.Cluster.join(:"their_app@192.168.1.42", cookie: :session_token)
# Now this works, across devices, over WiFi, with no server:<br>:rpc.call(:"their_app@192.168.1.42", TheirApp.GameServer, :move, [:left])<br>GenServer.call({MyServer, :"their_app@192.168.1.42"}, :get_state)This is not a protocol you built. It is not a WebSocket layer. It is Erlang<br>distribution — the same thing that has been running telecoms switches, trading<br>systems, and WhatsApp's backend (two million connections per server, in 2012,<br>on hardware that would embarrass a modern phone) for decades.<br>The implications for mobile:<br>Multiplayer without a server. Two phones, local network, no backend. Real<br>state synchronisation, not eventual consistency hacks.<br>Handoff. Start something on one device, continue on another. The state<br>is already there — it's just a pid on a different node.<br>Collaborative apps. Shared documents, live cursors, multi-user canvases —<br>built with the same primitives you use for everything else, not a specialised<br>CRDT library bolted on.<br>Device as a node in your backend cluster. The phone is not a client<br>polling an API. It is a peer in your OTP supervision tree. Your server can<br>call functions on the device as easily as the device calls functions on the<br>server.<br>The update story<br>Most apps treat an update as a full binary replacement — compile, submit, review,<br>release, hope users install it. Because the BEAM separates code from state, you<br>can push new modules to a running app and they take effect immediately. The<br>running processes pick up the new code on their next function call. No restart.<br>No lost session. No App Store wait for Elixir changes.<br>Combined with on-demand distribution (start a cluster connection, receive new<br>BEAMs, disconnect), OTA updates become a first-class feature rather than a<br>platform workaround.<br>Battery consumption<br>The BEAM has a reputation for being hard on mobile batteries. The numbers below<br>are measured on real hardware. All runs use the same conditions within each<br>mode so the results are directly comparable.<br>iOS (physical iPhone, screen on)<br>ModeStartEnd (30 min)DrainRateDefault (Nerves tuning)100%99%1%~2%/hrUntuned BEAM————Untuned run pending. Table will be updated.<br>How to read this: the BEAM with Nerves tuning costs ~2%/hr on a physical<br>iPhone with the screen on at minimum brightness — most of which is the screen<br>itself. The untuned row will show how much worse it gets without scheduler<br>tuning, which is what gives the BEAM its battery reputation.<br>iOS (physical iPhone, screen off, background)<br>Measured on a physical iPhone with the screen off the entire run, BEAM kept<br>alive via Mob's iOS background-audio keep-alive. Battery read every ~10 s via<br>mob_nif:battery_level/0; precise final reading via ideviceinfo (USB<br>connected briefly at end). All probe samples succeeded with the BEAM in<br>alive_rpc state for 100% of the...