Coding Complex App Logic, Visually

David Khourshid Β· @davidkpiano

Øredev Developer Conference 2022

<form onSubmit={event => {
  submitData(event);
}}>
  {/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);

<form onSubmit={event => {
  if (isLoading) { return; }

  submitData(event);

  setIsLoading(true);
}}>
  {/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);

<form onSubmit={event => {
  if (isLoading) { return; }

  submitData(event)
    .then(() => { setIsLoading(false) })

  setIsLoading(true);
}}>
  {/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);

<form onSubmit={event => {
  if (isLoading) { return; }

  submitData(event)
    .then(() => { setIsLoading(false) })
    .catch(err => {
      setIsLoading(false);
      setError(err);
    });

  setIsLoading(true);
}}>
  {/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
const [isCanceled, setIsCanceled] = useState(false);
const [error, setError] = useState(null);

<form onSubmit={event => {
  if (isLoading) { return; }

  submitData(event)
    .then(() => {
      if (isCanceled) { return; }
      setIsLoading(false)
    })
    .catch(err => {
      if (isCanceled) { return; }
      setIsLoading(false);
      setError(err);
    });

  setIsLoading(true);
}}>
  {/* ... */}
  <button
    type="button"
    onClick={() => { setIsCanceled(true) }}
  >
    Cancel
  </button>
  {/* ... */}
</form>

+ 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

Low-code

No thanks?

Graphics design

Games & CAD

Websites & Apps

πŸ“š

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

Demo

Statecharts

Extended state machines

idle

loading

LOAD / fetchData

success

RELOAD / fetchData

RESOLVE / assign

idle

loading

Β 

LOAD

success

RELOAD

RESOLVE / assign

entry / fetchData

exit / logTelemetry

  • ➑️ Transition actions
  • πŸ“₯ Entry actions
  • πŸ“€ Exit actions

Compound states

loading

ready

playing

paused

loaded

next

⏸

▢️

Parallel states

music

player

volume

playing

paused

muted

unmuted

Other features

πŸ›‘ Guarded transitions ⬆️ Raised events ⏱ Delayed (after) transitions ▢️ Eventless (always) transitions ⏳ History states ...

Demo

Callbacks

Event handlers

Ad-hoc logic

Ad-hoc logic

Reducer

EVENT

EVENT

EVENT

EVENT

State machine

EVENT

EVENT

EVENT

Statechart

Actors

What is an actor?

Actor πŸ‘©β€πŸ’»

Actor πŸ§”

βœ‰οΈ

βœ‰οΈ

someActor.send({
  type: 'greet',
  value: 'Hej!',
  from: self
});

// ...

switch (message.type) {
  case 'greet':
    message.from.send({
      type: 'greet',
      value: '...',
      from: self
    });
  // ...
}

Send

Receive

Rules of actors

πŸ€– All computation is performed within an actor

πŸ“© Actors can communicate only through messages

πŸ’₯ In response to a message, an actor can:

πŸ“¬ Send messages to other actors

πŸ‘¨β€πŸ‘§β€πŸ‘¦ Create a finite number of child actors

𝑓 (π‘₯)

Behavior

State

🎭 Change its state/behavior

Rules of actors

A

B

βœ‰οΈ

βœ‰οΈ

C

βœ‰οΈ

πŸ€– All computation is performed within an actor

πŸ“© Actors can communicate only through messages

πŸ’₯ In response to a message, an actor can:

πŸ“¬ Send messages to other actors

πŸ‘¨β€πŸ‘§β€πŸ‘¦ Create a finite number of child actors

🎭 Change its state/behavior

Rules of actors

A

βœ‰οΈ

πŸ€– All computation is performed within an actor

πŸ“© Actors can communicate only through messages

πŸ’₯ In response to a message, an actor can:

πŸ“¬ Send messages to other actors

πŸ‘¨β€πŸ‘§β€πŸ‘¦ Create a finite number of child actors

🎭 Change its state/behavior

A β†’ A'

Rules of actors

A

βœ‰οΈ

πŸ€– All computation is performed within an actor

πŸ“© Actors can communicate only through messages

πŸ’₯ In response to a message, an actor can:

πŸ“¬ Send messages to other actors

πŸ‘¨β€πŸ‘§β€πŸ‘¦ Create a finite number of child actors

🎭 Change its state/behavior

B

βœ‰οΈ

Rules of actors

A

βœ‰οΈ

πŸ€– All computation is performed within an actor

πŸ“© Actors can communicate only through messages

πŸ’₯ In response to a message, an actor can:

πŸ“¬ Send messages to other actors

πŸ‘¨β€πŸ‘§β€πŸ‘¦ Create a finite number of child actors

🎭 Change its state/behavior

πŸ†•

πŸ†•

πŸ‘©β€πŸ’»

πŸ§”

I would like a coffee...

What would you like?

Β ActorΒ 

I would like a coffee, please.

Β ActorΒ 

πŸ’­

πŸ’¬

πŸ’¬

Here you go. β˜•οΈ

Thanks!

πŸ‘©β€πŸ’»

πŸ§”

πŸ‘©β€πŸ³

β˜•οΈβ”

πŸ“

πŸ’Άβ“

πŸ’Ά

πŸ“„βœ…

β˜•οΈ

β˜•οΈ

Actor system architecture

Sequence diagrams

Map requirements to code

1

Code independently of frameworks

2

Make interfaces simple (read, send)

3

Literally profit

4

The future of development is visual ✨

Flowcharts

State machines

Statecharts

Sequence diagrams

Thank you Øredev!