Visualizing app logic with
state machines
David Khourshid ยท @davidkpiano
stately.ai
๏ฃฟ Frontend Friday
Code
+ Features
- Bugs
+/- Devs
+ Changes
+ Shiny new things
Time
Complexity
The path to
legacy code
Left to right
Top to bottom
File by file
Code is linear,
programs are not
๐
User stories
๐ผ
Mockups
๐
User flows
๐ป
Code
๐งช
Tests
๐ฉโ๐ซ
Tutorials
๐ฅ
๐
User stories
๐ผ
Mockups
๐
User flows
๐ป
Code
๐งช
Tests
๐ฉโ๐ซ
Tutorials
Designers
Stakeholders
Developers
Logged out
Logged in
LOG IN
[correct credentials]
- Make API call to Supabase
- Initiate OAuth flow
- Ensure token is valid
- Persist token as cookie
- Redirect to logged in view
Given a user is logged out,
When the user logs in with correct credentials
Then the user should be logged in
All programs are
directed graphs
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
- Initial state
- Finite set of states
- Finite set of events
- Transitions
- Final states
State machines prevent
impossible states.
isLoading isSuccess hasErrors
type Status = ย | 'loading' ย | 'error' ย | 'success'
function transition(state, event) {
switch (state.value) {
case 'cart':
if (event.type === 'CHECKOUT') {
return { value: 'shipping' };
}
return state;
case 'shipping':
// ...
default:
return state;
}
}
State machines
with switch statements
State
Event
const machine = {
initial: 'cart',
states: {
cart: {
on: {
CHECKOUT: 'shipping'
}
},
shipping: {
on: {
NEXT: 'contact'
}
},
contact: {
// ...
},
// ...
}
}
function transition(state, event) {
const nextState = machine
.states[state]
.on?.[event.type]
?? state;
}
transition('cart', { type: 'CHECKOUT' });
// => 'shipping'
transition('cart', { type: 'UNKNOWN' });
// => 'cart'
State machines
with objects
import { createMachine } from 'xstate';
const machine = createMachine({
initial: 'cart',
states: {
cart: {
on: {
CHECKOUT: 'shipping'
}
},
shipping: {
on: {
NEXT: 'contact'
}
},
contact: {
// ...
},
// ...
}
});
import { interpret } from 'xstate';
const actor = interpret(machine).start();
actor.send({ type: 'CHECKOUT' });
// => { value: 'shipping' }
actor.send({ type: 'NEXT' });
// => { value: 'contact' }
npm install xstate
xstate.js.org/docs
ย
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
BACK
BACK
CANCEL
Identify
logical flaws
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
As a user, when I'm in the cartย and I click the checkoutย button, I should be on the shippingย page.
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
As a user, when I'm in the cartย and I checkout via PayPal, I should be taken directly to the paymentย screen.
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
Shortest path
confirmationย state
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
Shortest path
confirmationย state where
shipping address is provided
Statecharts
Extended state machines
idle
loading
LOAD / fetchData
success
RELOAD / fetchData
RESOLVE / assign
Actions
idle
loading
ย
LOAD
success
RELOAD
RESOLVE / assign
entry / fetchData
exit / logTelemetry
- โก๏ธ Transition actions
- ๐ฅ Entry actions
- ๐ค Exit actions
Actions
start
Send for approval
sending
ย
Invocations
awaiting approval
ย
invoke / sendInvoice
invoke / waitForApproval
done (sendInvoice)
done (waitForApproval)
error
error (sendInvoice)
Retry
Compound states
loading
ready
playing
paused
loaded
next
โธ
โถ๏ธ
Parallel states
music
player
volume
playing
paused
muted
unmuted
PAUSE
PLAY
UNMUTE
MUTE
Other features
๐ Guarded transitions โฌ๏ธ Raised events โฑ Delayed (after) transitions โถ๏ธ Eventless (always) transitions โณ History states ...
Map requirements to code
1
Code independently of frameworks
2
Make interfaces simple (read, send)
3
Use a common visual language
4
Takeaways
Final presentation state.
Questions?
Visualizing app logic with state machines
By David Khourshid
Visualizing app logic with state machines
- 1,062