<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Erik Cameron</title>
    <description>The latest articles on Forem by Erik Cameron (@erikcameron).</description>
    <link>https://forem.com/erikcameron</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F799725%2F368eed04-9ae0-4663-911a-98d9f50fe4b5.jpeg</url>
      <title>Forem: Erik Cameron</title>
      <link>https://forem.com/erikcameron</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/erikcameron"/>
    <language>en</language>
    <item>
      <title>Practical State Machinery</title>
      <dc:creator>Erik Cameron</dc:creator>
      <pubDate>Wed, 29 Mar 2023 18:27:14 +0000</pubDate>
      <link>https://forem.com/thegnarco/practical-state-machinery-3aki</link>
      <guid>https://forem.com/thegnarco/practical-state-machinery-3aki</guid>
      <description>&lt;p&gt;As abstractions go, finite state machines represent a bit of low hanging fruit when you have real world problems to solve. The jargon can be a little forbidding—&lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/State_machine" rel="noopener noreferrer"&gt;MDN leads with “a mathematical abstraction used to design algorithms”&lt;/a&gt; and pretty much gets more technical from there—but in reality they represent a simple and practical technique for structuring code. This post is aimed at working engineers in the web and mobile world who want to understand the practical uses of state machines as a tool for software design. To that end, we’ll focus on concrete examples. The MDN link above provides a definition for the term “state machine;” we’re going to use it in a sentence.&lt;/p&gt;

&lt;p&gt;First question: What is a “state?” It’s a condition something is in, where that thing is always in &lt;em&gt;some&lt;/em&gt; condition out of a finite list at any given time. A car’s transmission might be in park, reverse or drive but not (we hope!) more than one at once. In software, the thing in question might be a blog post that’s in draft, published or deleted; it might be a user interface element that shows one pane on loading, one on success and another on error, with an option to retry; it might be code &lt;a href="https://users.cs.northwestern.edu/~agupta/cs340/project2/TCPIP_State_Transition_Diagram.pdf" rel="noopener noreferrer"&gt;deep in the network stack&lt;/a&gt;, ensuring handshakes are executed correctly. Many things you will have to model in software have states of this kind, implicitly or explicitly.&lt;/p&gt;

&lt;p&gt;“State machines” tell you, for a given thing, what state it should start in, and what the rules are for moving from state to state. For programmers, they’re a basic and powerful tool for imposing order on chaos. The benefits of state machines often stem from their declarative nature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validity&lt;/strong&gt;: Many bugs either never arise in the first place or turn into explicit errors with state machines. For example, an operation can be made to fail because it was called out of order, rather than being permitted to run with a &lt;code&gt;nil&lt;/code&gt; value that it expects an earlier operation to have set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity&lt;/strong&gt;: Machine definitions are relatively easy to visualize mentally, and literally using tools like &lt;code&gt;&lt;a href="https://graphviz.org/" rel="noopener noreferrer"&gt;graphviz&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt;: Machine definitions are also relatively easy to extend after the fact with new events and states. This is an important point because it’s reasonable for developers to worry that a state machine represents too much of an upfront design commitment. In cases where the machine represents complex business logic, the upfront commitment may well pay off later, when extending (or fixing) that logic is a matter of updating or creating a few definitions, rather than untangling a bowl of spaghetti code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polymorphism&lt;/strong&gt;: Software entities can, in general, dispatch to different implementations based on their current state, or even import entirely new interfaces. This can be tremendously useful, say, for conditional validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuhksmk8sqrefrr8medq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuhksmk8sqrefrr8medq.png" alt="The UI component described above, first as graphviz code, then as the resulting visualization."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In terms of implementation, state machines can take many forms. They might be abstracted by a software package, or you might roll your own, or you might just refer to some chunk of code as a “state machine” because that’s what it does. I’ll give examples in JSON, as objects of the following shape:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initial&lt;/code&gt;: The state the machine starts in, given as a string.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transitions&lt;/code&gt;: An object specifying how the machine changes. The keys of this object are events, given as strings. The values are themselves objects, where the key is a current state the machine might be in, and the value is the next state that event would put the machine in if it was.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example that models a light switch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;switch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This data structure, paired with some simple code to integrate it with your application, tells you pretty much everything you need to know about operating a light switch. It also provides a template for replacing (say) long blocks of conditional logic with something more declarative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Wizards
&lt;/h3&gt;

&lt;p&gt;Multipage forms, sometimes called “wizards”, are a common design pattern. They can also create headaches for web developers, as they require custom forms and logic based on the state of the user’s interaction. Here’s a typical and relatively simple checkout wizard you might use in an online store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;advance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cancel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirmation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The initial state will be &lt;code&gt;cart&lt;/code&gt; and will remain so while the user shops.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;checkout&lt;/code&gt; event can only occur while in state &lt;code&gt;cart&lt;/code&gt;, and when that event happens, we move to &lt;code&gt;shipping.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The user may &lt;code&gt;advance&lt;/code&gt; through the form when it’s in state &lt;code&gt;shipping&lt;/code&gt;, &lt;code&gt;billing&lt;/code&gt;, or &lt;code&gt;confirmation.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;From state &lt;code&gt;confirmation&lt;/code&gt; alone, the user can &lt;code&gt;confirm&lt;/code&gt; the order, at which point the payment will be run and the form will enter &lt;code&gt;accepted&lt;/code&gt;, a terminal state (because there’s no event that leads out of it to another state).&lt;/li&gt;
&lt;li&gt;If the user has started the checkout process but hasn’t finished it—i.e., if we’re in any state other than &lt;code&gt;cart&lt;/code&gt; or &lt;code&gt;accepted&lt;/code&gt;—we can cancel the checkout and continue shopping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine, in your language of choice, implementing the above with conditional logic. The user form itself must be rendered based on the state of the order, as will client- and server-side validations and side effects of successful operations, like sending confirmation emails or reindexing customer data. That’s four sites—rendering the form, running client-side validations, running server-side validations, dispatching backend side effects—where you’d potentially have &lt;code&gt;if&lt;/code&gt; or &lt;code&gt;case&lt;/code&gt; or &lt;code&gt;switch&lt;/code&gt; statements to maintain. Imagine also that the business team will inevitably come asking for a new screen in the middle of the wizard, with its own form, validation, side effects and so on, so all of this logic should be easier to refactor, for your own health if nothing else.&lt;/p&gt;

&lt;p&gt;Conditional logic to select a form component using JavaScript might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BillingForm&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ShippingForm&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code breaks encapsulation by making two things its own responsibility: the iteration logic (the list of case statements) and the dispatch logic (the connection between, e.g., &lt;code&gt;"billing"&lt;/code&gt; and &lt;code&gt;BillingForm&lt;/code&gt;). Suppose again we have four places where this or something like it happens in the application. Because the iteration logic is foisted on the caller, that’s four different lists of possible states. While one is updated, others might not be. From a design perspective it’s simply not the caller’s job to specify what the possible values are for the form, or what the connection is between those values and actual form components.&lt;/p&gt;

&lt;p&gt;It would be great if we could abstract/centralize both of those things, maybe by writing a little function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// like this:&lt;/span&gt;
&lt;span class="nf"&gt;getStateByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;
&lt;span class="c1"&gt;// or this:&lt;/span&gt;
&lt;span class="nf"&gt;getStateByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;validators&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hypothetical function would take the name of the current page and return an object  that provides what the caller really wants as simple keys. We’ll call these “state objects.” Callers don’t have to iterate over the possible states of the form, and state-specific information, like what form component to use, can stay encapsulated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* states/billingStage.js */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BillingForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;FormComponents&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pageName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BillingForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;field1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all you need to implement &lt;code&gt;getStateByName&lt;/code&gt; is a registry of state objects, keyed by &lt;code&gt;pageName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next page in the form is also dependent on the current state; when you leave &lt;code&gt;shipping&lt;/code&gt; you go to &lt;code&gt;billing&lt;/code&gt;, whereas when you leave &lt;code&gt;billing&lt;/code&gt; you go to &lt;code&gt;payment&lt;/code&gt;. Once you’ve started the checkout process you can &lt;code&gt;cancel&lt;/code&gt; it, but not after you’ve paid for it, i.e., if the state is &lt;code&gt;accepted&lt;/code&gt;. This logic will be used by the server to update the state, and by validators to tell the user if what they’re trying to do is permitted. Let’s add a &lt;code&gt;transitions&lt;/code&gt; key to our state object above to say what actions are possible, and what next state they will result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* states/billingState.js */&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;billingState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;advance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations, in the process of cleaning up conditional logic you have essentially implemented a state machine. With the addition of an initial state, (which you have already implemented elsewhere in your application, if the feature works at all) the &lt;code&gt;transitions&lt;/code&gt; objects of each state can easily be combined and transformed into the format above.&lt;/p&gt;

&lt;p&gt;The advantage of the format above, and of thinking in terms of state machines from the jump, is that it gives you an efficient, high-level representation of things you were probably going to do anyway, one way or another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Going Straight to the Cloud
&lt;/h3&gt;

&lt;p&gt;In the bad old days file upload was just a particularly wacky case of form submission, but not any more. As more applications make use of object storage services, it often makes sense for the user to upload the file directly to the object storage provider itself. For example, this saves the outbound bandwidth cost of transferring the file to object storage from your application servers. The question is, how do you track this in your application, now that the application servers never see the file?&lt;/p&gt;

&lt;p&gt;With help from the client, the application servers can track the state of the upload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;begin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user clicks a button to add a new file, triggering a call to the server for a new upload record. The server creates the record in state &lt;code&gt;pending&lt;/code&gt; and replies with a pre-signed URL in the object storage domain for the client to upload the file to. The client begins sending the file to the presigned URL, and sends the application server a &lt;code&gt;begin&lt;/code&gt; event, which updates the upload record. When the upload is complete, the client sends &lt;code&gt;done&lt;/code&gt;. If the pre-signed URL is rejected, the client can send a &lt;code&gt;reject&lt;/code&gt; event, or if the upload fails for some reason, it can send &lt;code&gt;fail&lt;/code&gt;. The client can &lt;code&gt;replace&lt;/code&gt; a file that’s been successfully uploaded, but not one that’s &lt;code&gt;failed&lt;/code&gt; because that doesn’t make sense. State machines are a serializable, language-agnostic way  for clients and servers to handle complex tasks over stateless media like HTTP.&lt;/p&gt;

&lt;p&gt;As in the previous case, the state machine itself is basically just a very compact representation of the business logic. That is: if you implement the business logic, &lt;a href="https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule" rel="noopener noreferrer"&gt;there’s a good chance you’ve implemented some kind of state machine, even if only implicitly&lt;/a&gt;. Using state machines by design makes the logic clear, and provides you with a consistent interface across your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating workflows
&lt;/h3&gt;

&lt;p&gt;Many applications also require substantial amounts of automation. A CI platform might have to respond to code pushes by building a Docker image, scheduling a container and running an automated test suite, for example, and every step will require customized error handling. State machines are ideal for modeling such job-based workflows.&lt;/p&gt;

&lt;p&gt;Let’s extend the second example to handle background work you might need to perform on an image upload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;begin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compression queue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compress complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compression queue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thumbnail queue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thumbnails complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thumbnail queue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, when the client reports &lt;code&gt;done&lt;/code&gt; the server will set the state. Let’s suppose that state lives in a cell in a database row, and that database is Postgres. Postgres has a feature called &lt;code&gt;&lt;a href="https://www.postgresql.org/docs/current/sql-notify.html" rel="noopener noreferrer"&gt;NOTIFY&lt;/a&gt;&lt;/code&gt; that can be used together with trigger functions to emit notifications on table updates. The &lt;code&gt;NOTIFY&lt;/code&gt; command requires two arguments, the channel and the payload. Let’s suppose further that we’ve configured a trigger function to emit a message to channel &lt;code&gt;upload-&amp;lt;upload id&amp;gt;&lt;/code&gt; whenever an upload record’s state changes, with the new state as the payload.&lt;/p&gt;

&lt;p&gt;Next, either using an off the shelf solution or your own concoction, get your application listening to Postgres notifications. Here’s an &lt;a href="https://blog.lelonek.me/listen-and-notify-postgresql-commands-in-elixir-187c49597851" rel="noopener noreferrer"&gt;example under Phoenix/Elixir from Kamil Lelonek&lt;/a&gt; that I found useful the first time I implemented this pattern. The idea is to associate certain states (say, &lt;code&gt;compression queue&lt;/code&gt; and &lt;code&gt;thumbnail queue&lt;/code&gt;) with job queues in your application. When your application receives a message to &lt;code&gt;upload-12345&lt;/code&gt; with payload &lt;code&gt;compress&lt;/code&gt;, it knows that &lt;code&gt;Upload&lt;/code&gt; &lt;code&gt;12345&lt;/code&gt; is ready to be shrunken from the massive TIFF sent by the user to something more manageable, and it can dispatch a worker to do so. When the worker is done, it sends the &lt;code&gt;compress complete&lt;/code&gt; event; the code integrating the state machine with your application updates the state to &lt;code&gt;thumbnail queue&lt;/code&gt;, and the process repeats with the thumbnail generation queue. Finally, when the record enters the &lt;code&gt;ready&lt;/code&gt; state, a notification is sent to clients that a new upload is in place, and no further processing occurs. (While the workers will have to pull the file from object storage, you can generally do so on nodes in the same infrastructure as your object storage and avoid being charged outbound bandwidth. Your main application servers can still be wherever you want, because the worker nodes need only communicate messages like &lt;code&gt;compress complete&lt;/code&gt; back to your database, the bandwidth for which is trivial compared to slinging the file back and forth.)&lt;/p&gt;

&lt;p&gt;If state changes trigger workers, and workers can make state changes, you can build automated workflows of arbitrary complexity. It bears mentioning that these workflows include error handling. The state machine can, for example, permit a &lt;code&gt;disk full error&lt;/code&gt;event when the image is being processed, but not when it’s in state &lt;code&gt;ready&lt;/code&gt;, when its disk usage doesn’t change. It also bears mentioning that workers can link different state machines across the application. On a &lt;code&gt;disk full error&lt;/code&gt;, the image worker might respond by putting another record governed by a different state machine into state &lt;code&gt;purge&lt;/code&gt; to call a worker to clear space. &lt;/p&gt;

&lt;h2&gt;
  
  
  When do you not want to use this?
&lt;/h2&gt;

&lt;p&gt;We said above that the kind of “state” we’re interested in here are kinds where an object is in one state at a time from a finite list. What does that rule out? Where &lt;em&gt;wouldn’t&lt;/em&gt; you see that?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The object has relevant state along more than one dimension:&lt;/strong&gt; Symptoms of this are where you start to condense multiple real world concepts into a single state field. Baseball games can happen at home or on the road, day or night, during any month of the season, etc. If you started modeling that and wound up with states like &lt;code&gt;june day home game&lt;/code&gt;, you may be trying to do too much with a single state attribute. On the other hand, if the dimensions are basically independent of each other, it may work to associate multiple state machines with a single entity. Returning to car transmissions as above, the transmission might be in &lt;code&gt;park&lt;/code&gt;, &lt;code&gt;drive&lt;/code&gt;, etc. along one dimension, it might be &lt;code&gt;brand new&lt;/code&gt; or &lt;code&gt;shopworn&lt;/code&gt; along another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The object doesn’t have any relevant state at all:&lt;/strong&gt; A basic web page with no drafting functionality probably doesn’t call for a state machine. It has &lt;em&gt;state&lt;/em&gt;, i.e., its contents, but doesn’t really move through a finite series of specific states; the usual CRUD model is sufficient to capture its dynamics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The changes in question are quantitative, rather than qualitative:&lt;/strong&gt; If the state in question comes in the form of a number, it might not be a great candidate for this abstraction. For example, you &lt;em&gt;could&lt;/em&gt; consider every value along the Celsius scale a state, and a panoply of events like &lt;code&gt;increase_by_one_degree&lt;/code&gt;, &lt;code&gt;increase_by_two_degrees&lt;/code&gt;, etc., but would that really be a good use of your time? (If on the other hand you’re ultimately tracking temperature because you want to know if a given piece of matter is &lt;code&gt;solid&lt;/code&gt;, &lt;code&gt;liquid&lt;/code&gt; or &lt;code&gt;gaseous&lt;/code&gt; then you have a good candidate for a state machine operated by the value of another column.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;State machines are an essential tool for managing complex behavior, and they are a mercifully simple one to integrate into your practice. The trick is knowing when to use them. There is almost certainly one or more state machine packages for your language of choice, so go see which ones people are using. We’ve linked to a few below. That, or create your own from scratch and tailor the integration to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.appsignal.com/2022/06/22/state-machines-in-ruby-an-introduction.html" rel="noopener noreferrer"&gt;State Machines in Ruby: An Introduction&lt;/a&gt; by Pulkit Goyal&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sebinbabu.com/posts/finite-state-machines-in-go/" rel="noopener noreferrer"&gt;Build and visualize finite state machines in Go&lt;/a&gt; by Sebin Babu&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jonbellah.com/articles/intro-state-machines" rel="noopener noreferrer"&gt;A Complete Introduction to State Machines in JavaScript&lt;/a&gt; by Jon Bellah&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://auth0.com/blog/state-pattern-in-python/" rel="noopener noreferrer"&gt;State Pattern in Python&lt;/a&gt; by Giridhar Talla&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/state-machines/state_machines" rel="noopener noreferrer"&gt;State Machines&lt;/a&gt; (Ruby) - A popular library providing a Ruby DSL for easily building finite state machines&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/statelyai/xstate" rel="noopener noreferrer"&gt;XState&lt;/a&gt; (JavaScript/TypeScript) - A full featured library providing visualization, test helpers, and integrations for popular frameworks like React and Vue&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/erikcameron/maxine" rel="noopener noreferrer"&gt;Maxine&lt;/a&gt; (Elixir) - The author’s own implementation based on the data shape used above, with Ecto integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Learn more about how The Gnar &lt;a href="https://www.thegnar.com/software-development" rel="noopener noreferrer"&gt;builds software&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>January Gnarly Learnings #1: Local services with asdf and foreman</title>
      <dc:creator>Erik Cameron</dc:creator>
      <pubDate>Wed, 19 Jan 2022 23:20:20 +0000</pubDate>
      <link>https://forem.com/thegnarco/january-gnarly-learnings-1-local-services-with-asdf-and-foreman-l1j</link>
      <guid>https://forem.com/thegnarco/january-gnarly-learnings-1-local-services-with-asdf-and-foreman-l1j</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WhpwapQc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewlfuo72wsudua2lfg67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WhpwapQc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewlfuo72wsudua2lfg67.png" alt="Image description" width="880" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon learning of &lt;code&gt;asdf&lt;/code&gt; plugins for backing services like Postgres, Redis, Elasticsearch and others, my first question was whether and to what extent it could replace Docker for providing those services in local development, where asdf already shines for versioning.&lt;/p&gt;

&lt;p&gt;The first obstacle for most users will be that &lt;code&gt;asdf&lt;/code&gt; has no equivalent of &lt;code&gt;docker compose up&lt;/code&gt; for booting and monitoring an entire local environment. (As of January 2022 &lt;a href="https://github.com/asdf-vm/asdf/discussions"&gt;the &lt;code&gt;asdf&lt;/code&gt; discussions page is recently dark&lt;/a&gt; but this was an active topic at the time of its disappearance.) Brainstorming the matter with a colleague, he pointed out we were essentially proposing Foreman. A few moments later he found this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kassioborges.com/2022/03/01/rails-project-development-dependencies.html_"&gt;Managing Ruby on Rails development dependencies with &lt;code&gt;asdf&lt;/code&gt; and &lt;code&gt;foreman&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[Note: We're talking about &lt;a href="https://github.com/ddollar/foreman"&gt;Foreman&lt;/a&gt;, not &lt;a href="https://github.com/theforeman/foreman"&gt;The Foreman&lt;/a&gt;.]&lt;/p&gt;

&lt;p&gt;From the oldie-but-a-goodie department: Foreman is a simple tool that reads a list of command line tasks from a file, starts them, feeds their output to &lt;code&gt;stdout&lt;/code&gt; with some nice formatting, and eventually shuts them down when you exit the process; i.e., exactly what &lt;code&gt;asdf&lt;/code&gt; doesn't provide. As outlined above, there isn't even really any "integration" necessary for &lt;code&gt;asdf&lt;/code&gt; and Foreman to play nicely. They just do their own things correctly.&lt;/p&gt;

&lt;p&gt;I've been using the approach above where possible for a couple of weeks now, and it's nice. (Be sure to note the trick in the blog post for running Postgres under Foreman control!) Docker is, among other things, a significant resource commitment for local development. On a Mac, you're spinning up an entire separate Linux instance to run software that MacOS is quite capable of running itself. This might be a good tradeoff; for many the benefit of standardizing developer experience or duplicating an idiosyncratic production environment will outweigh other considerations. But for individual developers looking for lightweight solutions—or to avoid duplicating functionality already provided by the kernel, the shell, &lt;code&gt;asdf&lt;/code&gt; and various language-specific dependency managers—this solution is very much worth a look.&lt;/p&gt;

&lt;h2&gt;Streamlining&lt;/h2&gt;

&lt;p&gt;I created the following in &lt;code&gt;~/bin/app&lt;/code&gt;, which will probably get (a little) more clever over time:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env zsh

foreman start -f Procfile.local&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I used &lt;code&gt;Procfile.local&lt;/code&gt; to prevent conflicts with applications that already have a &lt;code&gt;Procfile&lt;/code&gt; for production. In this case we're booting a Phoenix project using Postgres and Redis:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;postgres: mkdir -p log/ &amp;amp;&amp;amp; postgres-local
redis: redis-server
api: mix phx.server&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Meanwhile in &lt;code&gt;.tool-versions&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;elixir main-otp-24
erlang 24.2
postgres 14.1
redis 6.2.6&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Booting the application is nice and simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ app
17:05:22 postgres.1 | started with pid 12345
17:05:22 redis.1    | started with pid 12346
17:05:22 api.1      | started with pid 12347
[...logging continues, ctrl-c to exit...]&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;Future possibilities, difficulties, etc.&lt;/h2&gt;

&lt;p&gt;The main pain point I see is running multiple applications simultaneously. Docker virtualizes an entire operating system, so if you have two applications, A and B, which both use the same version of (say) Postgres, they still get entirely separate installations, data directories, port 5432 to bind, and so on. With this approach, you have one version of Postgres.&lt;/p&gt;

&lt;p&gt;One answer is to separate out the runtime specifics of the service from the codebase, which will presumably depend on the nature of the service itself, but follow a general shape:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Environment variables&lt;/li&gt;

    &lt;li&gt;Configuration files&lt;/li&gt;

    &lt;li&gt;Persistent data storage (your database)&lt;/li&gt;

    &lt;li&gt;Temporary data storage (logging, lock files)&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;If you can specify those uniquely for each instance in &lt;code&gt;Procfile.local&lt;/code&gt;, it's likely feasible to run multiple instances of the same service simultaneously. My own research into that will occur when/if I need to run several unrelated applications simultaneously, which is not usually how I work.&lt;/p&gt;

&lt;h2&gt;Credit where credit is due&lt;/h2&gt;

&lt;p&gt;Big h/t to Kassio Borges for having thought of this before any of us over here. That link again: &lt;a href="https://kassioborges.com/2021/03/01/rails-project-development-dependencies.html"&gt;Managing Ruby on Rails development dependencies with &lt;code&gt;asdf&lt;/code&gt; and &lt;code&gt;foreman&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>postgres</category>
      <category>tutorial</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
