Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
gen_statem - OTP's Unsung Hero
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Andrea Leopardi
April 04, 2025
Programming
370
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
gen_statem - OTP's Unsung Hero
Given at AlchemyConf 2025 in Braga, Portugal.
Andrea Leopardi
April 04, 2025
More Decks by Andrea Leopardi
See All by Andrea Leopardi
Agentic Elixir
whatyouhide
0
620
The Umbrella and the Range
whatyouhide
0
75
The World is a Network (and We Are Just Nodes)
whatyouhide
1
270
BEAM: The Perfect Fit for Networks
whatyouhide
1
280
Update from the Elixir team - 2022
whatyouhide
0
470
Testing Asynchronous OTP
whatyouhide
1
590
Elixir Sightseeing Tour
whatyouhide
0
510
Mint - Disrupting HTTP clients
whatyouhide
0
320
BEAM Architecture Handbook
whatyouhide
7
3.1k
Other Decks in Programming
See All in Programming
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
180
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
4
3.3k
HTML-in-Canvasで作る未来のWeb UI表現
clockmaker
5
5.8k
Apple Intelligenceとオンデバイス時代の到来
yahir0
0
150
sbt 2.0: takeoff
eed3si9n
0
170
ChromeでローカルLLM
ryu1013
0
160
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
240
AI時代のTerminal環境を UNIX哲学で設計・運用する
yasunori0418
1
270
Modernizing iOS Apps
seyfoyun
0
120
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.8k
Groovy as a human and AI friendly programming language
paulk
0
120
AmebaブログはKMPから卒業します
kmars0605
0
250
Featured
See All Featured
It's Worth the Effort
3n
188
29k
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
800
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
710
Unsuck your backbone
ammeep
672
58k
Thoughts on Productivity
jonyablonski
76
5.4k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
590
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
300
Transcript
None
Andrea Leopardi @whatyouhide
None
None
None
gen_statem
but why?!
FSM
FSM initial state states state transitions
None
chilling_and_looking_sad_for_no_reason
None
None
Finite?
Age of AI
gen_statem
None
@behaviour :gen_statem
None
init/1 terminate/3 code_change/4 format_status/2
def callback_mode do :state_functions end
handle_call/3 handle_cast/2 handle_info/2
<state_name>/3
<state_name>( event_type, event_content, data )
<state_name>( event_type, event_content, data )
<state_name>( event_type, event_content, data )
def handle_cast( {:put_coin, coin}, state ) do {:noreply, update_in(state.credit, &add_coin/1)}
end
def accepting_coins( :cast, {:put_coin, coin}, data ) do {:keep_state, update_in(data.credit,
&add_coin/1)} end
None
None
def accepting_coins( {:call, from}, {:press_button, beverage}, data ) do if
enough_credit?(data, beverage) do dispense_beverage(data, beverage) actions = [{:reply, from, :ok}] {:next_state, :dispensing, data, actions} else # ... end end
actions = [{:reply, from, :ok}] {:next_state, :dispensing, data, actions}
that’s kind of it?
Feature tour
Postponing events State enter callbacks Timeouts (state, event, named) Internal
events Non-finite state machines
Postponing events
None
def dispensing(:cast, {:add_coin, _}, data) do actions = [:postpone] {:keep_state_and_data,
actions} end def accepting_coins(:cast, {:add_coin, _}, data) do # Normal implementation end
“State enter”
def callback_mode do [ :state_functions, :state_enter ] end
def accepting_coins(:enter, _old_state, data) do update_display(data.credit) :keep_state_and_data end
Timeouts
Event timeouts
def accepting_coins(:cast, {:put_coin, coin}, data) do {:keep_state, update_in(data.credit, &add_coin/1)} end
def accepting_coins(:cast, {:put_coin, coin}, data) do actions = [ {:timeout,
to_timeout(second: 30), :give_back_coins} ] {:keep_state, update_in(data.credit, &add_coin/1), actions} end def accepting_coins(:timeout, :give_back_coins, data)
{:timeout, to_timeout(second: 30), :give_back_coins} def accepting_coins(:timeout, :give_back_coins, data)
State timeouts
def dispensing(:enter, _old_state, _data) do actions = [ {:state_timeout, to_timeout(minute:
1), :stuck} ] {:keep_state_and_data, actions} end def dispensing(:state_timeout, :stuck, data) do # Call for help, the machine got stuck. end
Named timeouts
def init(options) do actions = [ {{:timeout, :self_health_check}, to_timeout(hour: 1),
:no_content} ] {:ok, :idle, data, actions} end def idle({:timeout, :self_health_check}, _content, data) do # ... end
def accepting_coins({:timeout, :self_health_check}, _, _) do {:keep_state_and_data, [:postpone]} end def
dispensing({:timeout, :self_health_check}, _, _) do {:keep_state_and_data, [:postpone]} end
{:timeout, ms, content} {:state_timeout, ms, content} {{:timeout, :some_name}, ms, content}
“Internal” events
def disconnected({:timeout, :reconnect}, _, _data) do {:keep_state_and_data, {:next_event, :internal, :connect}}
end
Non-finite state machines
def callback_mode do :handle_event_function end
def accepting_coins( :cast, {:put_coin, coin}, data ) do # ...
end
def handle_event( :cast, {:put_coin, coin}, :accepting_coins, data ) do #
... end
def handle_event( event_type, event_content, current_state, data )
def handle_event( :cast, {:put_coin, coin}, {:accepting_coins, credit}, data ) do
new_state = {:accepting_coins, credit + coin} {:next_state, new_state, data} end
Use cases
None
GenServer :gen_statem or
def handle_call(call, from, %{user: nil}) def handle_call(call, from, %{user: %User{}})
def handle_call(call, from, %{user: %Admin{}})
def no_user({:call, from}, _, _) def user({:call, from}, _, _)
def admin({:call, from}, _, _)
Connections!
DBConnection Redix HTTP/2 (?)
None
state/named timeouts -> reconnection w/ backoff event timeouts -> keepalive
postponed events -> retrying requests
Workflow engines IoT sensors …
Wrapping up
Timeouts
No Elixir wrapper it’s fine
Data structures Processes vs
HTTP/2 stream state machine diagram
Composability
None
Resources Wikipedia Docs
Thanks! @whatyouhide AlchemyConf 2025