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