<?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: Mark Markaryan</title>
    <description>The latest articles on Forem by Mark Markaryan (@markmark).</description>
    <link>https://forem.com/markmark</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%2F123887%2F68ebb29f-9ebb-4979-93bf-650c77156242.jpeg</url>
      <title>Forem: Mark Markaryan</title>
      <link>https://forem.com/markmark</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/markmark"/>
    <language>en</language>
    <item>
      <title>Recruiting with AI and Elixir</title>
      <dc:creator>Mark Markaryan</dc:creator>
      <pubDate>Wed, 08 Apr 2026 10:14:03 +0000</pubDate>
      <link>https://forem.com/markmark/recruiting-with-ai-and-elixir-4cc5</link>
      <guid>https://forem.com/markmark/recruiting-with-ai-and-elixir-4cc5</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Source Code and Setup&lt;/li&gt;
&lt;li&gt;Herbarium, Inc. Needs Help&lt;/li&gt;
&lt;li&gt;The Candidate Workflow Graph&lt;/li&gt;
&lt;li&gt;Talking to AI: The 3 Functions&lt;/li&gt;
&lt;li&gt;Let's Try It!&lt;/li&gt;
&lt;li&gt;Next Steps&lt;/li&gt;
&lt;li&gt;"SNORK RECRUITING DOES NOT SCALE"?&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Build Something Useful!
&lt;/h2&gt;

&lt;p&gt;At this point, you might be wondering, "Sure, the useless machine was fun to make, and to see what's happening behind the scenes is super-neat, but can we build something a bit more... useful?"&lt;/p&gt;

&lt;p&gt;Absolutely! Let's build a recruiting website that uses AI to review submissions and filters out spam and mismatches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code and Setup
&lt;/h2&gt;

&lt;p&gt;The source code for this exercise is available at &lt;a href="https://github.com/shipworthy/recruit" rel="noopener noreferrer"&gt;https://github.com/shipworthy/recruit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can clone the repo and run the code on your machine, or you can just follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Herbarium, Inc. Needs Help
&lt;/h2&gt;

&lt;p&gt;Business is booming at Herbarium, Inc. The pile of plants to be examined is growing. &lt;/p&gt;

&lt;p&gt;Mr. Hemulen would love some help, but looking for someone is just too much work. To make things worse, last time he tried, he got buried under a mountain of applications that all looked so... meaningless. As if they were all written by a trembling herd of electricity-hungry Hattifatteners. No good.&lt;/p&gt;

&lt;p&gt;Snork, a famed inventor, has a solution to Mr. Hemulen's problem. AI!!&lt;/p&gt;

&lt;p&gt;Mr. Hemulen will provide a job description and Snork's tool will give him candidates that seem real and look like a potential fit for the job!&lt;/p&gt;

&lt;p&gt;Easy! We just need to come up with a way to evaluate and curate submissions!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Candidate Workflow Graph: Components
&lt;/h2&gt;

&lt;p&gt;Excited, Snork starts sketching the steps of the workflow.&lt;/p&gt;

&lt;p&gt;Some of the steps are inputs, some are computations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Inputs
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;input&lt;/code&gt;s for the workflow are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the job description&lt;/li&gt;
&lt;li&gt;the resume&lt;/li&gt;
&lt;li&gt;the time of the submission
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Computations
&lt;/h3&gt;

&lt;p&gt;Once all the &lt;code&gt;input&lt;/code&gt;s are in place, the workflow will &lt;code&gt;compute&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;before doing anything else, is this a real resume? and, if so,&lt;/li&gt;
&lt;li&gt;the summary of the candidate's background, as it applies to Mr. Hemulen's job description.&lt;/li&gt;
&lt;li&gt;the match score, 0..100 – is the candidate a good fit for the job?
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:resume_valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;is_resume_valid&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:resume_summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;summarize_resume&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:match_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;compute_match_score&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hemulen-in-the-Middle Input
&lt;/h3&gt;

&lt;p&gt;After reviewing the submission, Mr. Hemulen can &lt;code&gt;input&lt;/code&gt; his &lt;code&gt;decision&lt;/code&gt; for next steps. Chat with the candidate? Or no?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Candidate Workflow Graph: All Together Now
&lt;/h2&gt;

&lt;p&gt;This all seems pretty straightforward! &lt;/p&gt;

&lt;p&gt;Excited, Snork puts the whole thing together.&lt;/p&gt;

&lt;p&gt;A couple of things become apparent in the process: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;each of the &lt;code&gt;compute&lt;/code&gt; nodes is unblocked when its prerequisites are met (e.g. &lt;code&gt;:match_score&lt;/code&gt; is unblocked – and its function gets called by Journey – when &lt;code&gt;:resume_valid&lt;/code&gt; becomes true).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;thinking ahead, when something changes (e.g. we have the &lt;code&gt;:match_score&lt;/code&gt;!), we'll want to let the candidate's web page know, so it can update itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;since LLM processing (computing a summary or scoring) can take a while, we'll give those computations a more generous &lt;code&gt;abandon_after_seconds&lt;/code&gt; than the default of 60 seconds.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the graph that Snork came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;"candidate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:resume_valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;unblocked_when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:and&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="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;is_resume_valid&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:resume_summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;unblocked_when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:and&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="ss"&gt;:resume_valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;true?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;summarize_resume&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;abandon_after_seconds:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:match_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;unblocked_when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;:and&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="ss"&gt;:resume_valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;true?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;compute_match_score&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;abandon_after_seconds:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;execution_id_prefix:&lt;/span&gt; &lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;f_on_save:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;execution_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"f_on_save[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;execution_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]: candidate updated, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;node_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="no"&gt;ResumeScreener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"candidate:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;execution_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:refresh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="ss"&gt;:ok&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;f_on_save&lt;/code&gt;: When Something Changes!
&lt;/h2&gt;

&lt;p&gt;At the end of the graph, Snork added a bit of code to generate a PubSub message whenever any of the &lt;code&gt;input&lt;/code&gt; or &lt;code&gt;compute&lt;/code&gt;'d values get updated. The UI can listen to the message, and update itself whenever something changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talking to AI: The 3 Functions
&lt;/h2&gt;

&lt;p&gt;All that's left is to implement the three functions attached to the &lt;code&gt;compute&lt;/code&gt; nodes!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_resume_valid/1&lt;/code&gt; &lt;br&gt;
the function that looks at the submission and determines if it's an actual resume.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;summarize_resume/1&lt;/code&gt;&lt;br&gt;
the function that looks at the submitted resume and summarizes and evaluates it in the context of the job description.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;compute_match_score/1&lt;/code&gt;&lt;br&gt;
the function that computes a score for "is the submission a good match for the job", from 0 (nope) to 100 (this is the perfect candidate for the job, at least according to the submission).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first function, &lt;code&gt;is_resume_valid/1&lt;/code&gt;, uses the &lt;code&gt;facebook/bart-large-mnli&lt;/code&gt; model, executed via &lt;a href="https://hexdocs.pm/bumblebee" rel="noopener noreferrer"&gt;bumblebee&lt;/a&gt; / &lt;a href="https://github.com/elixir-nx/nx" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; to perform zero-shot classifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="nv"&gt;@resume_label&lt;/span&gt; &lt;span class="s2"&gt;"a professional resume describing a person's professional background and experiences"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;classifier_serving&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Loading classifier model..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bumblebee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:hf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"facebook/bart-large-mnli"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Bumblebee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_tokenizer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:hf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"facebook/bart-large-mnli"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="n"&gt;serving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;Bumblebee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero_shot_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nv"&gt;@resume_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"not a resume"&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Classifier model loaded"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;serving&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;is_resume_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resume_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"checking resume for validity"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;truncated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sanitize_and_truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resume_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Serving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batched_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ResumeScreener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ResumeClassifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;truncated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;resume_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predictions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;@resume_label&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resume_score&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes &lt;code&gt;is_resume_valid/1&lt;/code&gt; extremely inexpensive and quick to run.&lt;/p&gt;

&lt;p&gt;If the resume is deemed valid, we then proceed to summarization and scoring.&lt;/p&gt;

&lt;p&gt;These are more expensive operations, for which we use an actual LLM, &lt;code&gt;gemma3:4b&lt;/code&gt;, hosted by Ollama. You can see these functions implemented in &lt;a href="https://github.com/shipworthy/recruit/blob/main/lib/resume_screener/candidate_graph.ex" rel="noopener noreferrer"&gt;https://github.com/shipworthy/recruit/blob/main/lib/resume_screener/candidate_graph.ex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a &lt;code&gt;compute&lt;/code&gt; node's &lt;code&gt;unblocked_when&lt;/code&gt; conditions are met, Journey calls its compute function, with all of the execution's values, expecting the function to return &lt;code&gt;{:ok, computed_value}&lt;/code&gt;. For more information about &lt;code&gt;f_compute&lt;/code&gt; functions, see &lt;a href="https://hexdocs.pm/journey/Journey.Node.html#compute/4" rel="noopener noreferrer"&gt;Journey documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Try It!!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Application 1: "zap barometer zap zap"
&lt;/h3&gt;

&lt;p&gt;Snork will use his graph to power his new recruiting website, but for now, he just wants to try it in IEx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;~/src/resume_screener $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ResumeScreener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Candidate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mr. Hemulen has provided his job description, and someone emailed their application ("How did they get my email address?").&lt;/p&gt;

&lt;p&gt;Snork is eager to use his new system to review the submission!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_job_description.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_resume1.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;os_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:second&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as Snork "submits" the application, &lt;code&gt;:resume_valid&lt;/code&gt; node becomes unblocked, and Journey calls its function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;09:03:38.579 [info] checking resume for validity
09:03:40.144 [info] f_on_save[C303R5YZYMVXXYGG4RETR]: candidate updated, resume_valid, {:ok, false}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...wait a second. This is not a valid resume? What is it??&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resume_valid?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_revision&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume_valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait:&lt;/span&gt; &lt;span class="ss"&gt;:any&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;resume_valid?&lt;/span&gt;
&lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_revision&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait:&lt;/span&gt; &lt;span class="ss"&gt;:any&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;resume&lt;/span&gt;
&lt;span class="s2"&gt;"zap barometer zap zap&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh. Right.&lt;/p&gt;

&lt;p&gt;A little disappointing, but also... reassuring – this is what the system is supposed to do!&lt;/p&gt;

&lt;h3&gt;
  
  
  Application 2: "i would like to write my memoir."
&lt;/h3&gt;

&lt;p&gt;Snork didn't have to wait long for another submission. Let's see what we got!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;~/src/resume_screener $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ResumeScreener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Candidate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_job_description.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_resume2.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;os_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:second&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see!!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;10:05:41.875 [info] checking resume for validity
10:05:43.480 [info] f_on_save[CXZH2T9J5XV5B3XEDMR41]: candidate updated, resume_valid, {:ok, true}
10:05:43.492 [info] summarizing resume for candidate
10:05:43.492 [info] computing match score for candidate
10:05:54.163 [info] resume summary composed for candidate
10:05:54.179 [info] f_on_save[CXZH2T9J5XV5B3XEDMR41]: candidate updated, resume_summary, {:ok, "1.  **Overview:** Moomin Papa presents as a self-described writer and philosopher with limited formal experience. His background is primarily focused on personal adventures and a memoir project.\n\n2.  **Impressive Skills/Deliverables:** Adventuring (a unique selling point), writing skills, and a personal project (Substack).\n\n3.  **Relevant Skills:** None explicitly relevant to the job description. \n\n4.  **Missing Skills:** Extensive experience (14+ years) in application development, particularly with Elixir.  Lack of experience with IoT, Cloud Computing, and Big Data, which are key to Kelvin’s work. No demonstrable leadership or mentoring experience.\n\n5.  **Things to Ask:** What specific types of writing experience does he have? Can he quantify his “adventuring” experience and relate it to problem-solving?  Describe his familiarity with Elixir beyond the substack.  How does he handle ambiguity and complex challenges?"}
10:05:55.415 [info] match score computed for candidate, 15
10:05:55.421 [info] f_on_save[CXZH2T9J5XV5B3XEDMR41]: candidate updated, match_score, {:ok, 15}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;YES!! We have a real resume. But why is the match score so low?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_revision&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait:&lt;/span&gt; &lt;span class="ss"&gt;:any&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;resume&lt;/span&gt;
&lt;span class="s2"&gt;"Moomin Papa&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;papa@valley.com | Moomin Valley | substack/adventures_in_the_valley&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Summary&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;A writer philosopher with a lifetime of adventures is writing a memoir.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Skills:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Writing, adventuring.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;EXPERIENCE&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Present: a writer.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;References available upon request.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh. Ok, Moomin Papa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application 102: Hm.
&lt;/h3&gt;

&lt;p&gt;After a few more of those cryptic "zap barometer zap zap"-style submissions, and a "low-match-score" application from Snorkmaiden (I don't care what AI says, I love you sis! ❤️), Snork is looking at application number 102.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;~/src/resume_screener $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ResumeScreener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Candidate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_job_description.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"example_resume3.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:submitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;os_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:second&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So exciting!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;10:25:49.118 [info] checking resume for validity
10:25:51.878 [info] f_on_save[CX9LH56G7V8M4GH9E71TZ]: candidate updated, resume_valid, {:ok, true}
10:25:51.889 [info] summarizing resume for candidate
10:25:51.889 [info] computing match score for candidate
10:25:59.252 [info] match score computed for candidate, 92
10:25:59.266 [info] f_on_save[CX9LH56G7V8M4GH9E71TZ]: candidate updated, match_score, {:ok, 92}
10:26:07.485 [info] resume summary composed for candidate
&lt;/span&gt;&lt;span class="gp"&gt;10:26:07.501 [info] f_on_save[CX9LH56G7V8M4GH9E71TZ]: candidate updated, resume_summary, {:ok, "1. **General Overview:** Morgan Elix is a highly experienced Staff Software Engineer with over 12 years specializing in highly concurrent, fault-tolerant distributed systems, primarily using Elixir and OTP. &amp;lt;...&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have our first candidate with a high match score!! Snork is so excited to pass Morgan's application to Mr. Hemulen!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Mr. Hemulen was excited to see Morgan's application. They had a good chat.&lt;/p&gt;

&lt;p&gt;Morgan did not end up becoming Mr. Hemulen's assistant. Morgan wanted to put all the plants in rented buckets in "the cloud", and to charge everyone for looking at pictures of the plants. Mr. Hemulen didn't quite understand how this would make anything better. "This would just make EVERYTHING worse," he thought.&lt;/p&gt;

&lt;p&gt;But Mr. Hemulen liked having an easy way to connect with qualified potential helpers, and he wanted to keep the system going.&lt;/p&gt;

&lt;p&gt;Snork didn't want to keep running workflows by hand, so he put together a website around it. The website lets candidates upload their resumes, and shows Mr. Hemulen the list of real qualified candidates, with match scores and notes for each.&lt;/p&gt;

&lt;p&gt;Snork was kind enough to open-source the website: &lt;a href="https://github.com/shipworthy/recruit" rel="noopener noreferrer"&gt;https://github.com/shipworthy/recruit&lt;/a&gt;, complete with usage instructions in its README.md.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peer Review: "SNORK RECRUITING DOES NOT SCALE"
&lt;/h2&gt;

&lt;p&gt;It was spring, and Snufkin was visiting the Valley. He was curious about Snork's invention, and he seemed to really like it, but he also said "Snork Recruiting does not scale" before wandering off.&lt;/p&gt;

&lt;p&gt;This made Snork think. "What did Snufkin mean? How would I 'scale' this?"&lt;/p&gt;

&lt;p&gt;Stay tuned for the next blog! : )&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Resume Screener on GitHub: &lt;a href="https://github.com/shipworthy/recruit" rel="noopener noreferrer"&gt;https://github.com/shipworthy/recruit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey on Hex: &lt;a href="https://hexdocs.pm/journey" rel="noopener noreferrer"&gt;https://hexdocs.pm/journey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey on GitHub: &lt;a href="https://github.com/shipworthy/journey" rel="noopener noreferrer"&gt;https://github.com/shipworthy/journey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey's Website: &lt;a href="https://gojourney.dev" rel="noopener noreferrer"&gt;https://gojourney.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cover image: generated by Google Gemini, seemingly trained on Moomin characters. Moomin character rights are owned by Moomin Characters Oy Ltd., &lt;a href="https://www.moomin.com/" rel="noopener noreferrer"&gt;https://www.moomin.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>elixir</category>
      <category>journey</category>
      <category>durableworkflow</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Mystery of a Missing Greeting</title>
      <dc:creator>Mark Markaryan</dc:creator>
      <pubDate>Thu, 11 Dec 2025 04:51:44 +0000</pubDate>
      <link>https://forem.com/markmark/the-mystery-of-a-missing-greeting-3ebf</link>
      <guid>https://forem.com/markmark/the-mystery-of-a-missing-greeting-3ebf</guid>
      <description>&lt;p&gt;"Sure, I can build things quickly with durable workflows, but how do I debug them?"&lt;/p&gt;

&lt;p&gt;I hear this question a lot, so let's solve this mystery!&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code and Setup
&lt;/h2&gt;

&lt;p&gt;The source code for this exercise is available at &lt;a href="https://github.com/shipworthy/journey_introspect" rel="noopener noreferrer"&gt;github.com/shipworthy/journey_introspect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can clone that repo and follow its README's setup steps to run this sequence on your machine, or you can just follow along!&lt;/p&gt;

&lt;p&gt;Some output in fragments below is omitted for brevity.&lt;/p&gt;

&lt;p&gt;With logistics out of the way, let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome, Customers!
&lt;/h2&gt;

&lt;p&gt;You are a technical co-founder. You just implemented the "welcome" sequence of your onboarding application, as a durable workflow. Your co-founder is surprised at how quickly you did it and how well it works.&lt;/p&gt;

&lt;p&gt;Honestly, you are also surprised at how little code you needed to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;
&lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;"Customer Onboarding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:email_address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:greeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email_address&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;welcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Welcome, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; at &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email_address&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&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 application spins up an execution of this workflow (&lt;a href="https://hexdocs.pm/journey/Journey.html#start/1" rel="noopener noreferrer"&gt;&lt;code&gt;Journey.start(graph)&lt;/code&gt;&lt;/a&gt;) for every customer, as they navigate your website. Customers are getting greeted. The business is booming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mystery of a Missing Greeting
&lt;/h2&gt;

&lt;p&gt;All of a sudden you get a call from your co-founder, Ms. Too-Ticky: "Mr. Hemulen, our newest customer, did not get his 'welcome' greeting! Why?!?"&lt;/p&gt;

&lt;p&gt;No worries, Ms. Too-Ticky! &lt;a href="https://hexdocs.pm/journey/Journey.Tools.html#introspect/1" rel="noopener noreferrer"&gt;&lt;code&gt;Journey.Tools.introspect/1&lt;/code&gt;&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;h2&gt;
  
  
  Mystery Solved?
&lt;/h2&gt;

&lt;p&gt;Let's introspect Mr. Hemulen's onboarding execution. Did &lt;code&gt;:greeting&lt;/code&gt; compute?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;introspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="ss"&gt;Values:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;Set:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'"Hemulen"'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:input&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;rev:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;Not&lt;/span&gt; &lt;span class="ss"&gt;set:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;email_address:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unk&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:input&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;greeting:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unk&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:compute&lt;/span&gt;

&lt;span class="ss"&gt;Computations:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;Outstanding:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;greeting:&lt;/span&gt; &lt;span class="err"&gt;⬜&lt;/span&gt; &lt;span class="ss"&gt;:not_set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;yet&lt;/span&gt; &lt;span class="n"&gt;attempted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:compute&lt;/span&gt;
       &lt;span class="ss"&gt;:and&lt;/span&gt;
        &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="err"&gt;✅&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="err"&gt;🛑&lt;/span&gt; &lt;span class="ss"&gt;:email_address&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;provided?&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aha! &lt;code&gt;:greeting&lt;/code&gt; is blocked – we have Mr. Hemulen's &lt;code&gt;:name&lt;/code&gt;, but not his &lt;code&gt;:email_address&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mystery Solved! Hello, Mr. Hemulen!
&lt;/h2&gt;

&lt;p&gt;As soon as Mr. Hemulen provides his email address, he is greeted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:email_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"hemulen@gojourney.dev"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Hemulen&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;hemulen&lt;/span&gt;&lt;span class="nv"&gt;@gojourney&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All done!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:greeting&lt;/code&gt; should now be computed. Is it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;introspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="ss"&gt;Values:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;Set:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;greeting:&lt;/span&gt; &lt;span class="s1"&gt;'"Welcome, Hemulen at hemulen@gojourney.dev"'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:compute&lt;/span&gt;
    &lt;span class="n"&gt;computed&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;rev:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;email_address:&lt;/span&gt; &lt;span class="s1"&gt;'"hemulen@gojourney.dev"'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:input&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;rev:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'"Hemulen"'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:input&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="no"&gt;Z&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;rev:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="ss"&gt;Computations:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;Completed:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="ss"&gt;:greeting&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CMPR7RL0T7T2VTJAG9Z0748&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="err"&gt;✅&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="ss"&gt;:compute&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="ss"&gt;used:&lt;/span&gt;
       &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="ss"&gt;:email_address&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes! With Mr. Hemulen's &lt;code&gt;:name&lt;/code&gt; and &lt;code&gt;:email_address&lt;/code&gt; both in place, &lt;code&gt;:greeting&lt;/code&gt; is done!&lt;/p&gt;

&lt;p&gt;Ms. Too-Ticky is happy. Mr. Hemulen is happy.&lt;/p&gt;

&lt;p&gt;One call to &lt;a href="https://hexdocs.pm/journey/Journey.Tools.html#introspect/1" rel="noopener noreferrer"&gt;&lt;code&gt;Journey.Tools.introspect/1&lt;/code&gt;&lt;/a&gt;... mystery solved!&lt;/p&gt;

&lt;p&gt;Stay tuned for more adventures in Durable Workflows. ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The source code for this walkthrough: &lt;a href="https://github.com/shipworthy/journey_introspect" rel="noopener noreferrer"&gt;https://github.com/shipworthy/journey_introspect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey Library: &lt;a href="https://hexdocs.pm/journey" rel="noopener noreferrer"&gt;https://hexdocs.pm/journey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cover image: generated by Google Gemini, seemingly trained on Moomin characters. Moomin character rights are owned by Moomin Characters Oy Ltd., &lt;a href="https://www.moomin.com/" rel="noopener noreferrer"&gt;https://www.moomin.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>elixir</category>
      <category>journey</category>
      <category>debugging</category>
      <category>durableworkflow</category>
    </item>
    <item>
      <title>Building a Useless Machine in Elixir</title>
      <dc:creator>Mark Markaryan</dc:creator>
      <pubDate>Thu, 27 Nov 2025 04:54:42 +0000</pubDate>
      <link>https://forem.com/markmark/building-a-useless-machine-in-elixir-42i1</link>
      <guid>https://forem.com/markmark/building-a-useless-machine-in-elixir-42i1</guid>
      <description>&lt;p&gt;A Useless Machine does one thing: when you turn it on, a mechanical paw reaches out and flips the switch back off. Useless, but fun.&lt;/p&gt;

&lt;p&gt;Let's build one in Elixir!&lt;/p&gt;

&lt;p&gt;To save us some code, and to give our Useless Machine object permanence, we'll define it as a reactive durable workflow using &lt;a href="https://hexdocs.pm/journey" rel="noopener noreferrer"&gt;Journey&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prerequisites
&lt;/h2&gt;

&lt;p&gt;To play with this, you'll want to have Elixir 1.18 or 1.19, Docker (if you want to run your postgres database in a docker container), and some familiarity with Elixir.&lt;/p&gt;

&lt;p&gt;If you use &lt;a href="https://asdf-vm.com/guide/getting-started.html" rel="noopener noreferrer"&gt;&lt;code&gt;asdf&lt;/code&gt;&lt;/a&gt;, here is the &lt;code&gt;.tool-versions&lt;/code&gt; file I used for this exercise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;$ &lt;span class="n"&gt;cat&lt;/span&gt; .&lt;span class="n"&gt;tool&lt;/span&gt;-&lt;span class="n"&gt;versions&lt;/span&gt;
&lt;span class="n"&gt;erlang&lt;/span&gt; &lt;span class="m"&gt;27&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;elixir&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;19&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;-&lt;span class="n"&gt;otp&lt;/span&gt;-&lt;span class="m"&gt;27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Source Code
&lt;/h2&gt;

&lt;p&gt;The source code for this exercise (+ a test) is available at &lt;a href="https://github.com/shipworthy/useless_machine" rel="noopener noreferrer"&gt;https://github.com/shipworthy/useless_machine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to just start playing with Useless Machine, not implement it, just clone the repo, follow its setup steps, and fast forward to section &lt;code&gt;5. Useless Machine: Let's Play&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to follow along... well, follow along. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create and Configure the Project
&lt;/h2&gt;

&lt;p&gt;We'll start by creating a basic Elixir project, wiring up its dependencies (postgres, journey), configuring its database and journey logging, fetching dependencies, and creating the database. &lt;/p&gt;

&lt;p&gt;This is largely standard Elixir stuff, so I'll list the steps, but won't spend much time discussing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the initial Elixir project:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix new useless_machine
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;useless_machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wire Up Dependencies
&lt;/h3&gt;

&lt;p&gt;Update your new project's &lt;code&gt;mix.exs&lt;/code&gt; to add Journey, Ecto, and Postgrex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:journey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.10.40"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ecto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.12 or ~&amp;gt; 3.13"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:postgrex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.20 or ~&amp;gt; 0.21"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure the Database and Logging
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;config/config.exs&lt;/code&gt;, with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:useless_machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ecto_repos:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:journey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;database:&lt;/span&gt; &lt;span class="s2"&gt;"useless_machine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;username:&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;hostname:&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;:console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;format:&lt;/span&gt; &lt;span class="s2"&gt;"$time [$level] $metadata$message&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;level:&lt;/span&gt; &lt;span class="ss"&gt;:warning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;metadata:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:mfa&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fetch Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix deps.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start a Postgres Instance, Create the Database
&lt;/h3&gt;

&lt;p&gt;Start a Postgres instance in a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;-d&lt;/span&gt; postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix ecto.create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Implement the Useless Machine
&lt;/h2&gt;

&lt;p&gt;Now that we have the placeholder project in place, let's implement our Useless Machine.&lt;/p&gt;

&lt;p&gt;A Useless Machine may look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ┌─────────────────────────────────────────────┐
  │                                             │
  │  ┌────────────┐             ┌────────────┐  │
  │  │            │ 2. triggers │            │  │
  │  │  :switch   │────────────▶│    :paw    │  │
  │  │  (input)   │             │  (mutate)  │  │
  │  │            │◀────────────│            │  │
  │  └────────────┘ 3. "off"    └────────────┘  │
  │        ▲                                    │
  └────────┼────────────────────────────────────┘
           │ 1. "on"
       ┌───┴───┐
       │ user  │
       └───────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luckily, it will only be a few lines of code!&lt;/p&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;lib/useless_machine.ex&lt;/code&gt; with these 15 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;UselessMachine&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Node&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_graph&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:paw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lol_no&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;mutates:&lt;/span&gt; &lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;lol_no&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="ss"&gt;switch:&lt;/span&gt; &lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"paw says: '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;? lol no'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This module defines&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;a graph with two nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input(:switch)&lt;/code&gt; — an input node, representing the switch, which we will turn &lt;code&gt;"on"&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mutate(:paw, [:switch], &amp;amp;lol_no/1, mutates: :switch)&lt;/code&gt; — triggered on changes in &lt;code&gt;:switch&lt;/code&gt;, it &lt;code&gt;mutate&lt;/code&gt;s the value of &lt;code&gt;:switch&lt;/code&gt;, setting it to the value returned by &lt;code&gt;lol_no/1&lt;/code&gt; (&lt;code&gt;"off"&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the function, &lt;code&gt;lol_no/1&lt;/code&gt;, which &lt;code&gt;:paw&lt;/code&gt; will execute whenever the &lt;code&gt;:switch&lt;/code&gt; is turned &lt;code&gt;"on"&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This defines your Useless Machine!&lt;/p&gt;

&lt;p&gt;One last thing we need to do is to register the graph with Journey by adding these lines to &lt;code&gt;config/config.exs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:journey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:graphs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;UselessMachine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Useless Machine: Let's Play
&lt;/h2&gt;

&lt;p&gt;Now that we have everything in place, let's play with our Useless Machine!&lt;/p&gt;

&lt;p&gt;Let's fire up &lt;code&gt;iex&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;iex&lt;/code&gt;, let's create a new Useless Machine execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UselessMachine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turn the &lt;code&gt;:switch&lt;/code&gt; on!! ... and watch &lt;code&gt;:paw&lt;/code&gt; promptly turn the &lt;code&gt;:switch&lt;/code&gt; off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;paw&lt;/span&gt; &lt;span class="ss"&gt;says:&lt;/span&gt; &lt;span class="s1"&gt;'on? lol no'&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;switch:&lt;/span&gt; &lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;paw:&lt;/span&gt; &lt;span class="s2"&gt;"updated :switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;execution_id:&lt;/span&gt; &lt;span class="s2"&gt;"EXECRDYHBJ8X0RVD8BG0A7A5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;last_updated_at:&lt;/span&gt; &lt;span class="mi"&gt;1764109938&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Was it a fluke? Let's try again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;paw&lt;/span&gt; &lt;span class="ss"&gt;says:&lt;/span&gt; &lt;span class="s1"&gt;'on? lol no'&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;switch:&lt;/span&gt; &lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;paw:&lt;/span&gt; &lt;span class="s2"&gt;"updated :switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;execution_id:&lt;/span&gt; &lt;span class="s2"&gt;"EXECRDYHBJ8X0RVD8BG0A7A5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;last_updated_at:&lt;/span&gt; &lt;span class="mi"&gt;1764109952&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, not a fluke.&lt;/p&gt;

&lt;p&gt;No matter how many times you set the switch to &lt;code&gt;"on"&lt;/code&gt;, the paw immediately turns it back off.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Useless Machine: &lt;strong&gt;Durable&lt;/strong&gt;!
&lt;/h2&gt;

&lt;p&gt;Because Journey workflows are durable, our Useless Machine execution persists across time and reboots.&lt;/p&gt;

&lt;p&gt;Let's try it!  &lt;/p&gt;

&lt;p&gt;Take note of the id of your Useless Machine execution (e.g. &lt;code&gt;EXECRDYHBJ8X0RVD8BG0A7A5&lt;/code&gt;), quit &lt;code&gt;iex&lt;/code&gt; (Ctrl+C twice), then start a fresh session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load your old Useless Machine by its ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"EXECRDYHBJ8X0RVD8BG0A7A5"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and make sure that the Useless Machine is just as we left it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;execution_id:&lt;/span&gt; &lt;span class="s2"&gt;"EXECRDYHBJ8X0RVD8BG0A7A5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;last_updated_at:&lt;/span&gt; &lt;span class="mi"&gt;1764109952&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;paw:&lt;/span&gt; &lt;span class="s2"&gt;"updated :switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;switch:&lt;/span&gt; &lt;span class="s2"&gt;"off"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like before, as soon as you turn the &lt;code&gt;:switch&lt;/code&gt; &lt;code&gt;"on"&lt;/code&gt;, the &lt;code&gt;:paw&lt;/code&gt; flips it &lt;code&gt;"off"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;paw&lt;/span&gt; &lt;span class="ss"&gt;says:&lt;/span&gt; &lt;span class="s1"&gt;'on? lol no'&lt;/span&gt;
&lt;span class="ss"&gt;:ok&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Journey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;%{&lt;/span&gt;
  &lt;span class="ss"&gt;switch:&lt;/span&gt; &lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;paw:&lt;/span&gt; &lt;span class="s2"&gt;"updated :switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;execution_id:&lt;/span&gt; &lt;span class="s2"&gt;"EXECRDYHBJ8X0RVD8BG0A7A5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;last_updated_at:&lt;/span&gt; &lt;span class="mi"&gt;1764110367&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution survived a restart. It is just as useless as before. ;) &lt;/p&gt;

&lt;h2&gt;
  
  
  7. Wrap-up
&lt;/h2&gt;

&lt;p&gt;In under 20 lines of code, we built a (durable, crash-safe, and scalable 😂!) Useless Machine!&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Links
&lt;/h2&gt;

&lt;p&gt;Things referenced in this walkthrough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elixir Programming Language: &lt;a href="https://elixir-lang.org" rel="noopener noreferrer"&gt;https://elixir-lang.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey on Hex: &lt;a href="https://hexdocs.pm/journey" rel="noopener noreferrer"&gt;https://hexdocs.pm/journey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey on GitHub: &lt;a href="https://github.com/shipworthy/journey" rel="noopener noreferrer"&gt;https://github.com/shipworthy/journey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Journey's Website: &lt;a href="https://gojourney.dev" rel="noopener noreferrer"&gt;https://gojourney.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Useless Machine on GitHub: &lt;a href="https://github.com/shipworthy/useless_machine" rel="noopener noreferrer"&gt;https://github.com/shipworthy/useless_machine&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cover image: generated by Google Gemini, seemingly trained on Moomin characters. Moomin character rights are owned by Moomin Characters Oy Ltd., &lt;a href="https://www.moomin.com/" rel="noopener noreferrer"&gt;https://www.moomin.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>elixir</category>
      <category>journey</category>
      <category>uselessmachine</category>
      <category>durableworkflow</category>
    </item>
  </channel>
</rss>
