
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[David Mosher's Blog]]></title><description><![CDATA[thoughts on software, music, design, and meaning]]></description><link>https://blog.davemo.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 16 Feb 2026 18:10:35 GMT</lastBuildDate><atom:link href="https://blog.davemo.com/index.xml" rel="self" type="application/rss+xml"/><author><![CDATA[David Mosher]]></author><item><title><![CDATA[Deterministic Core, Agentic Shell]]></title><description><![CDATA[<aside class="tldr">State machines are the answer for those seeking determinism in the era of AI agents.</aside>

<p>The release of Gary Bernhardt’s screencast <a href="https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell">Functional Core, Imperative Shell</a> mentioned at the end of his talk <a href="https://www.destroyallsoftware.com/talks/boundaries">Boundaries</a> was a transformational moment for me. Up until that point in my career, I hadn’t really thought too much about “architecture” as it relates to software, well at least not in the way I had heard software architects speak about it, which was, to my eager-but-naive ears, filled with a bunch of buzzwords and acronyms that I did not understand.</p>
<p>I have returned to both the screencast and talk many times because the concepts are <em>packed</em> with wisdom that continues to shape my thinking; it is evergreen content, you should watch both.</p>
<p>Gary’s insight was that separating the pure from the effectful lets you test what matters and push complexity to the edges. I think we’re at a similar inflection point now — but the “shell” has become something far more unpredictable than imperative I/O. The shell is now an LLM.</p>
<h2 id="where-i-learned-state-machines">Where I Learned State Machines</h2>
<p>From ~2008-2011 I worked at my first startup, <a href="https://www.vendasta.com">Vendasta Technologies</a>. They’re still around, and doing well, and dare I say had some of the best startup marketing of the 2000’s. Seriously just watch this 40 second spot riffing on how we named the company and tell me you didn’t laugh out loud …</p>
<iframe id="vendasta-name" width="100%" height="350" src="https://www.youtube.com/embed/ntT7v47RIds" frameborder="0" allowfullscreen></iframe>

<p>The beauty of startups is twofold in my mind: you get to work with great people and you are forced to wear many hats. A few of those great people are folks who I deeply respect and admire, who have all gone onto greater things:</p>
<ul>
<li><a href="https://www.linkedin.com/in/kevpie/">Kevin Pierce</a> was the person who got me into software development as a career in the first place and graciously spent time mentoring me at coffee-shops in Saskatoon, SK, answering all of my questions and helping me connect the dots when all I really knew was web frontend tech.</li>
<li><a href="https://www.linkedin.com/in/shawnrusaw/">Shawn Rusaw</a> introduced me to the idea of expressing workflows and logic via finite state machines (FSMs) and did deep research into whitepapers on FSM architecture that shaped how our team built systems.</li>
<li><a href="https://www.linkedin.com/in/jasonacollins/">Jason Collins</a> was the CTO who led all of our teams and made all of the experimental ideas come to life knowing exactly how to put all the pieces together.</li>
</ul>
<p>One of the highlights of working at Vendasta was the chance to get exposure to a ton of ideas; state machines resonated with me in particular because of how elegantly they enabled complex workflows to be built on top of simple concepts.</p>
<p>An early project the team built called <a href="https://web.archive.org/web/20110808134612/http://www.mashedin.com/">MashedIn</a> — which mashed your accounts from Twitter, LinkedIn, and Facebook to surface common connections — was where I began to connect the dots on these concepts.</p>
<p>MashedIn needed async workflows — fan-out across multiple social media APIs, reconciliation, deduplication — and the team built an async state-machine workflow engine in Python called <strong>Fantasm</strong> to power them. I recall eagerly watching demos and listening to Shawn speak about how it all fit together.</p>
<p>It was the first thing we <a href="https://code.google.com/archive/p/fantasm/">open-sourced</a> as a company, and Shawn, Kevin, and Jason were instrumental in making it happen on the beta version of AppEngine (what would eventually become a part of GCP).</p>
<p>It got so popular at the time that it was <a href="https://cloudplatform.googleblog.com/2011/03/implementing-workflows-on-app-engine.html">featured on the AppEngine blog</a>. You can see <a href="https://github.com/vendasta/fantasm/tree/master">how it has evolved on GitHub.</a></p>
<p>I want to <em>underscore</em> this was back in <strong>2011</strong>!</p>
<p><img src="/img/deterministic-core-agentic-shell/mashedin-circa-2011.png" alt="MashedIn Circa 2011"></p>
<aside>I have fond memories of debugging early versions of AppEngine, trying to figure out whether it made more sense to use Google's thin <a href='https://thescoop.org/archives/2010/02/23/a-gentle-introduction-to-google-app-engine'>webapp</a> framework or fight with Django; fun times.</aside>

<h2 id="foundations-of-state-machines">Foundations of State Machines</h2>
<p>Finite state machines trace back to the 1950s and 60s — <a href="https://en.wikipedia.org/wiki/Mealy_machine">Mealy (1955)</a> and <a href="https://en.wikipedia.org/wiki/Moore_machine">Moore (1956)</a> formalized the two canonical FSM models, and they became foundational in compiler design, protocol specification, and hardware engineering. Shawn and the team at Vendasta took those ideas and made them practical for web-scale async workflows.</p>
<p>Fantasm’s FSM implementation was inspired by a later paper, published in 1999 by Jilles van Gurp and Jan Bosch: <a href="http://www.jillesvangurp.com/static/fsm-sea99.pdf">On the Implementation of Finite State Machines</a>, and a talk by Brett Slatkin at Google I/O 2010 titled <a href="https://www.youtube.com/watch?v=zSDC_TU7rtc">Building high-throughput data pipelines with Google AppEngine</a>.</p>
<iframe id="slatkin-talk" width="100%" height="350" src="https://www.youtube.com/embed/zSDC_TU7rtc" frameborder="0" allowfullscreen></iframe>

<p>I re-read the <a href="http://www.jillesvangurp.com/static/fsm-sea99.pdf">paper</a> this morning, and what struck me was how much it anticipates what came later. Their assessment section walks through how easy it is to add a new state — you just add a line of XML and retarget some transitions. </p>
<p>Their future work section even calls out conditional transitions (“transitions that only occur if the trigger event occurs and the condition holds true”) and Statechart support (“normal FSMs + nesting + orthogonality + broadcasting events”) — which is literally what <a href="https://xstate.js.org/">XState</a> implements today with guards, nested states, parallel states, and the <code>sendTo/raise</code> event system. </p>
<p>They also call out the need for “tracing and debugging tools” to keep track of what’s happening in the context — which is pretty much where XState’s <a href="https://stately.ai/docs/inspector">Stately Inspector</a> and <a href="https://stately.ai/docs/studio">Stately Studio</a> have landed, giving you live statechart visualizations and sequence diagrams, but I digress.</p>
<p>The point I really wanted to make was, since my time at Vendasta, I have <em>repeatedly</em> returned to the concepts of finite state machines, and often found myself migrating business logic that was in the form of a poorly-specified-hidden-half-baked state machine into an actual state machine with transitions, guards, and easily unit-tested logic. </p>
<p><strong>If there’s a golden hammer I think is actually worth swinging at every codebase, it’s definitely state machines.</strong></p>
<p>(And if this wasn’t enough to summon <a href="https://x.com/DavidKPiano">David K. Piano</a>, I don’t know what is). 😛</p>
<h2 id="the-pattern-repeats">The Pattern Repeats</h2>
<p>Five years ago I was working at <a href="https://www.surveymonkey.com/">SurveyMonkey</a> as part of the Analyze team. The survey product was composed of a few pieces that fit together in a workflow: </p>
<p><code>Design -&gt; Preview -&gt; Collect -&gt; Analyze -&gt; Present</code></p>
<p>At the time, my team was tasked with coming up with a vision for how we could improve the Analyze experience for users; it had been built with a custom JavaScript framework that was difficult to follow, frequently violated <a href="https://htmx.org/essays/locality-of-behaviour/">Locality of Behaviour</a> due to sprawl, had very fuzzy architectural boundaries, and made heavy use of event-driven architecture that was opaque. </p>
<p>Events fired randomly, state changed, and debugging was an exercise in frustration, requiring a detailed specification of “the framework” open and a lot of trial and error to trace code execution when debugging. I ended up building a proof-of-concept using <a href="https://xstate.js.org/">XState</a> <code>v4.26.1</code> during one of our hackathons, and I recall thinking:</p>
<blockquote>
<p>this entire workflow, end to end, feels like it should be a state machine, what would it look like if we had survey creators use a GUI to produce a state machine definition that we could serialize in our db as the source of truth, and then when survey takers go to take the survey, we reinflate that definition and have them run it live in their browser</p>
</blockquote>
<p>I was pretty convinced this could work, and the POC provided enough direction to give me confidence.</p>
<p>The scenario I tested with was a school allergy survey. Three questions, starting with one about chocolate bars. The idea was that a survey author could configure conditional branching logic in the GUI — if a respondent picked a chocolate bar that didn’t contain nuts then the follow-up question about nut allergies wouldn’t apply and should be skipped. </p>
<p>Simple enough to explain, but it maps perfectly to a state machine: each question is a state, each answer is an event, and branching logic is a guard.</p>
<aside>This is pretty much the same kind of conditional transition that van Gurp & Bosch flagged as "hard to implement in an OO way" in the future work section of their whitepaper. They suggested using the <a href='https://refactoring.guru/design-patterns/command'>Command pattern</a> from the Gang of Four to create condition objects — XState has <a href='https://stately.ai/docs/guards'>guards</a> as a first-class concept. Same problem, solved 20 years later with a cleaner primitive.</aside>

<p>Because I like code, here’s the core of the POC (syntax is XState <code>v4</code>, so it’s old and out of date compared to <code>v5</code>, but illustrative enough to get the gist):</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// xstate v4 syntax from ~5 years ago</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Machine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;xstate&#x27;</span>

<span class="hljs-keyword">const</span> surveyTakingMachine = <span class="hljs-title class_">Machine</span>({
  <span class="hljs-attr">id</span>: <span class="hljs-string">&#x27;survey&#x27;</span>,
  <span class="hljs-attr">initial</span>: <span class="hljs-string">&#x27;q1&#x27;</span>,
  <span class="hljs-attr">context</span>: {
    <span class="hljs-attr">answerOptions</span>: {
      <span class="hljs-attr">q1</span>: [<span class="hljs-string">&#x27;Snickers&#x27;</span>, <span class="hljs-string">&#x27;Big Turk&#x27;</span>, <span class="hljs-string">&#x27;Oh Henry&#x27;</span>],
      <span class="hljs-attr">q3</span>: [<span class="hljs-string">&#x27;$0.50 USD&#x27;</span>, <span class="hljs-string">&#x27;$0.75 USD&#x27;</span>, <span class="hljs-string">&#x27;$1.00 USD&#x27;</span>],
    },
  },
  <span class="hljs-attr">states</span>: {
    <span class="hljs-attr">q1</span>: { <span class="hljs-comment">// What is your favorite chocolate bar?</span>
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">RECEIVE_ANSWER</span>: [
          {
            <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;q3&#x27;</span>,
            <span class="hljs-attr">cond</span>: <span class="hljs-function">(<span class="hljs-params">context, event</span>) =&gt;</span> {
              <span class="hljs-comment">// no nuts → skip the nut allergy question</span>
              <span class="hljs-keyword">return</span> event.<span class="hljs-property">answer</span> === context.<span class="hljs-property">answerOptions</span>[<span class="hljs-string">&#x27;q1&#x27;</span>][<span class="hljs-number">1</span>]
            },
          },
          { <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;q2&#x27;</span> },
        ],
      },
    },
    <span class="hljs-attr">q2</span>: { <span class="hljs-comment">// What is your schools policy on peanut allergies?</span>
      <span class="hljs-attr">on</span>: { <span class="hljs-attr">RECEIVE_ANSWER</span>: [{ <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;q3&#x27;</span> }] },
    },
    <span class="hljs-attr">q3</span>: { <span class="hljs-comment">// What is a fair price for a chocolate bar?</span>
      <span class="hljs-attr">on</span>: { <span class="hljs-attr">RECEIVE_ANSWER</span>: [{ <span class="hljs-attr">target</span>: <span class="hljs-string">&#x27;completed&#x27;</span> }] },
    },
    <span class="hljs-attr">completed</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;final&#x27;</span> },
  },
})
</code></pre>
<p>The builder (“config”) side dynamically generated these machine configs from user input. You’d add questions in a UI, define answer options, and input branching logic as a text expression like <code>Q1 = C2 =&gt; SKIP TO P3</code> (if Question 1, Choice 2 is selected, skip to Page 3).</p>
<p>Obviously this was a POC — a production version would need a more robust expression language — but for the purposes of proving the concept it was sufficient. Every change regenerated the machine config in real-time — you could see the JSON update as you built. The config was then serialized to storage.</p>
<p><img src="/img/deterministic-core-agentic-shell/xstate-survey-builder.png" alt="The survey builder"></p>
<p>The survey taker doesn’t know there’s a state machine. They just see questions, one at a time, with branching that “just works”.</p>
<p><img src="/img/deterministic-core-agentic-shell/xstate-survey-taker.png" alt="The survey taker + debug"></p>
<p>My design notes from the time are interesting to revisit in the age of AI:</p>
<pre><code><span class="hljs-attribute">SurveyTakingOneAtATimeMachine
SurveyTakingClassicMachine
SurveyTakingConversationMachine</span>
</code></pre>
<p>A conversational survey (<code>SurveyTakingConversationMachine</code>) is, in hindsight, just a voice agent running a state machine. I just didn’t have the voice agent yet.</p>
<p>The POC didn’t seem to get much traction internally, which had me scratching my head — the approach seemed like such a natural fit to me. Maybe the idea was too ahead of its time, maybe XState wasn’t mature enough for the team, or maybe I just wasn’t good at pitching. 😅</p>
<p>A more likely reason is that my POC hadn’t fully solved the serialization/runtime challenges; I had conceptualized how it would work — a machine definition as a JSON blob serialized in your database and inflated at runtime — but I didn’t show how that could happen aside from a basic demo using <code>localStorage</code>.</p>
<p>XState <code>v4</code> had not yet formalized use of the Actor model, which sits beautifully on top of a state machine; in <code>v5</code>, XState made this explicit — every running machine <em>is</em> an actor, and you get <code>getPersistedSnapshot()</code> to serialize the full actor state and restore it later with <code>createActor(machine, { snapshot: restored })</code>. </p>
<p>In 1999, van Gurp &amp; Bosch were solving the same problem with XML and Java serialization:</p>
<blockquote>
<p>FSMAction components are instantiated, configured and saved to a file using serialization. The saved files are referred to from the XML file as .ser files. When the framework is configured the .ser files are deserialized and plugged into the FSM framework.</p>
</blockquote>
<p>The round-trip from database to runtime and back that they were building with <code>XML</code> and <code>.ser</code> files is the same round-trip XState <code>v5</code> gives you with <code>JSON</code> and <code>getPersistedSnapshot()</code>.</p>
<h2 id="deterministic-core-agentic-shell">Deterministic Core, Agentic Shell</h2>
<p>I keep coming back to this idea of core/shell as I repeatedly see the pattern of legacy applications that could be simplified by using a state machine in the core.</p>
<p>Which brings me back to where I started: Gary defined the functional core and imperative shell as a pattern that informed the type of code and approach to testing that I have seen work extremely well in practice. I think we are now entering a time where we can apply a similar architectural lens to apps that leverage LLMs. </p>
<p>Because I lack originality, and I really like Gary, I’m calling it <strong>deterministic core, agentic shell</strong>.</p>
<p>The trick as I see it is, all these companies want to build “agentic” features and rely heavily on the LLM, which frequently (and rightfully) violate every software engineer’s sensibilities around determinism. Evals, LLM-as-judge, vibes-based-testing, and all of the “rigor” around how to “test” prompts and use of agents is all well and good, but for my money nothing beats the fast feedback loop and confidence of a sufficiently isolated unit test with pure functions.</p>
<p>So, similar to how functional core was Gary’s answer to testability in a world full of side effects, my assertion is that state machines are the answer to determinism in the era of AI agents. I have seen time and again that if we draw a larger box around that core and try as hard as possible to shove all the things that are important into it, and into a state machine, that the layers above (both imperative and agentic) become minimized, reducing risk, and making it much easier to verify correctness in the core of the system.</p>
<h2 id="a-pragmatic-reference-architecture">A Pragmatic Reference Architecture</h2>
<p>I’ve been spiking on a project with <a href="https://www.linkedin.com/in/jon-girard-8239988b/">Jon Girard</a> and <a href="https://www.linkedin.com/in/miketimko/">Michael Timko</a> and applying these ideas — the heritage from van Gurp &amp; Bosch, the configuration-driven approach from Fantasm, the machine-as-source-of-truth thinking from my SurveyMonkey POC — and it is proving highly effective at validating <strong>deterministic core, agentic shell</strong>. </p>
<p>Here’s the broad strokes of what we’ve been working with:</p>
<pre><code>+--------------------------------------------------+
|<span class="hljs-string">  Telnyx (telephony)                              </span>|
|<span class="hljs-string">  caller &lt;-&gt; WebSocket &lt;-&gt; u-law 8kHz audio       </span>|
+--------------------------------------------------+
|<span class="hljs-string">  Agentic Shell                                   </span>|
|<span class="hljs-string">                                                  </span>|
|<span class="hljs-string">  Mastra Agent + OpenAI Realtime (voice LLM)      </span>|
|<span class="hljs-string">  - system prompt constrained by machine state    </span>|
|<span class="hljs-string">  - available tools constrained by machine state  </span>|
|<span class="hljs-string">                    </span>|<span class="hljs-string">                             </span>|
|<span class="hljs-string">                    </span>|<span class="hljs-string"> tool calls                  </span>|
|<span class="hljs-string">                    v                             </span>|
|<span class="hljs-string">  Mastra Tools (bridge)                           </span>|
|<span class="hljs-string">  - translate agent intent -&gt; machine event       </span>|
|<span class="hljs-string">  - return machine state -&gt; agent                 </span>|
+--------------------------------------------------+
|<span class="hljs-string">  Deterministic Core                              </span>|
|<span class="hljs-string">                    </span>|<span class="hljs-string">                             </span>|
|<span class="hljs-string">                    </span>|<span class="hljs-string"> events                      </span>|
|<span class="hljs-string">                    v                             </span>|
|<span class="hljs-string">  XState Machine                                  </span>|
|<span class="hljs-string">  - states, transitions, guards, actions          </span>|
|<span class="hljs-string">  - pure functions, trivially testable            </span>|
+--------------------------------------------------+
|<span class="hljs-string">  Postgres (storage)                              </span>|
+--------------------------------------------------+
</code></pre>
<p>If you have worked with <a href="https://mastra.ai/">Mastra</a> before, there are some great framework primitives that will let you build a workflow, agents, and even individual tool calls; it’s very easy to put all your eggs in the Mastra basket, and it is <em>good</em> at what it does, but one of my hard-learned engineering principles is it’s better to <em>minimize</em> coupling to 3rd party systems and tools and put as much of your business logic into code that you own. </p>
<p>When the “system” is a sprawl of connections to 3rd party services or libraries, the surface area of your codebase becomes harder to test, change, and that much more of a liability.</p>
<p>Jason Collins, the CTO at Vendasta I mentioned earlier, used to say <strong>“show me the code!”</strong> whenever someone pitched an idea in a meeting. </p>
<p>I’ve always loved that — so in that spirit, here’s some code!</p>
<h2 id="a-deterministic-core">A Deterministic Core</h2>
<p>Let’s use a simple identity verification flow as our example — the kind of thing you’d encounter at the start of any phone-based workflow. </p>
<p>Everything important lives in the machine definition:</p>
<pre><code class="hljs language-typescript"><span class="hljs-comment">// xstate 5 syntax</span>
<span class="hljs-keyword">import</span> { assign, setup } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;xstate&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sessionMachine = <span class="hljs-title function_">setup</span>({
  <span class="hljs-attr">types</span>: {
    <span class="hljs-attr">context</span>: {} <span class="hljs-keyword">as</span> {
      <span class="hljs-attr">attempts</span>: <span class="hljs-built_in">number</span>;
      <span class="hljs-attr">verifiedId</span>: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
    },
    <span class="hljs-attr">events</span>: {} <span class="hljs-keyword">as</span> { <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;VERIFY&quot;</span>; <span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span> },
  },
  <span class="hljs-attr">guards</span>: {
    <span class="hljs-attr">isValidId</span>: <span class="hljs-function">(<span class="hljs-params">{ event }</span>) =&gt;</span> event.<span class="hljs-property">id</span> === <span class="hljs-string">&quot;6789&quot;</span>,
  },
  <span class="hljs-attr">actions</span>: {
    <span class="hljs-attr">incrementAttempts</span>: <span class="hljs-title function_">assign</span>({
      <span class="hljs-attr">attempts</span>: <span class="hljs-function">(<span class="hljs-params">{ context }</span>) =&gt;</span> context.<span class="hljs-property">attempts</span> + <span class="hljs-number">1</span>,
    }),
    <span class="hljs-attr">setVerifiedId</span>: <span class="hljs-title function_">assign</span>({
      <span class="hljs-attr">verifiedId</span>: <span class="hljs-function">(<span class="hljs-params">{ event }</span>) =&gt;</span> event.<span class="hljs-property">id</span>,
    }),
  },
}).<span class="hljs-title function_">createMachine</span>({
  <span class="hljs-attr">id</span>: <span class="hljs-string">&quot;session&quot;</span>,
  <span class="hljs-attr">initial</span>: <span class="hljs-string">&quot;greeting&quot;</span>,
  <span class="hljs-attr">context</span>: { <span class="hljs-attr">attempts</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">verifiedId</span>: <span class="hljs-literal">null</span> },
  <span class="hljs-attr">states</span>: {
    <span class="hljs-attr">greeting</span>: {
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">VERIFY</span>: [
          {
            <span class="hljs-attr">guard</span>: <span class="hljs-string">&quot;isValidId&quot;</span>,
            <span class="hljs-attr">target</span>: <span class="hljs-string">&quot;verified&quot;</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">&quot;incrementAttempts&quot;</span>, <span class="hljs-string">&quot;setVerifiedId&quot;</span>],
          },
          {
            <span class="hljs-attr">target</span>: <span class="hljs-string">&quot;awaitingVerification&quot;</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">&quot;incrementAttempts&quot;</span>],
          },
        ],
      },
    },
    <span class="hljs-attr">awaitingVerification</span>: {
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">VERIFY</span>: [
          {
            <span class="hljs-attr">guard</span>: <span class="hljs-string">&quot;isValidId&quot;</span>,
            <span class="hljs-attr">target</span>: <span class="hljs-string">&quot;verified&quot;</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">&quot;incrementAttempts&quot;</span>, <span class="hljs-string">&quot;setVerifiedId&quot;</span>],
          },
          { <span class="hljs-attr">actions</span>: [<span class="hljs-string">&quot;incrementAttempts&quot;</span>] },
        ],
      },
    },
    <span class="hljs-attr">verified</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;final&quot;</span> },
  },
});
</code></pre>
<p>The guard (<code>isValidId</code>) is a pure function. The actions are deterministic assignments. The transitions are explicit. You can unit test this without any LLM, any network, any voice connection. Deterministic core.</p>
<h2 id="bridge-tools-that-connect-agent-to-machine">Bridge: Tools That Connect Agent to Machine</h2>
<p>The LLM doesn’t decide if the user is verified. The machine does. A Mastra <a href="https://mastra.ai/docs/agents/using-tools">tool</a> is the thin bridge between them:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> verifyIdentityTool = <span class="hljs-title function_">createTool</span>({
  <span class="hljs-attr">id</span>: <span class="hljs-string">&quot;verify-identity&quot;</span>,
  <span class="hljs-attr">description</span>:
    <span class="hljs-string">&quot;Verify the user&#x27;s identity by checking their 4-digit verification ID.&quot;</span>,
  <span class="hljs-attr">inputSchema</span>: z.<span class="hljs-title function_">object</span>({
    <span class="hljs-attr">id</span>: z.<span class="hljs-title function_">string</span>().<span class="hljs-title function_">describe</span>(<span class="hljs-string">&quot;The 4-digit verification ID provided by the user&quot;</span>),
  }),
  <span class="hljs-attr">execute</span>: <span class="hljs-keyword">async</span> ({ context, runtimeContext }) =&gt; {
    <span class="hljs-keyword">const</span> sessionActor = runtimeContext.<span class="hljs-title function_">get</span>(<span class="hljs-string">&quot;sessionActor&quot;</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">SessionActor</span>;

    <span class="hljs-comment">// Agent -&gt; event to the machine — machine decides what happens</span>
    sessionActor.<span class="hljs-title function_">send</span>({ <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;VERIFY&quot;</span>, <span class="hljs-attr">id</span>: context.<span class="hljs-property">id</span> });

    <span class="hljs-comment">// Machine state is the source of truth</span>
    <span class="hljs-keyword">const</span> snapshot = sessionActor.<span class="hljs-title function_">getSnapshot</span>();
    <span class="hljs-keyword">const</span> verified = snapshot.<span class="hljs-property">value</span> === <span class="hljs-string">&quot;verified&quot;</span>;

    <span class="hljs-keyword">return</span> {
      verified,
      <span class="hljs-attr">message</span>: verified
        ? <span class="hljs-string">&quot;Identity verified successfully.&quot;</span>
        : <span class="hljs-string">`Incorrect verification ID. (Attempt <span class="hljs-subst">${snapshot.context.attempts}</span>)`</span>,
      <span class="hljs-attr">attempts</span>: snapshot.<span class="hljs-property">context</span>.<span class="hljs-property">attempts</span>,
    };
  },
});
</code></pre>
<p>The tool translates what the agent heard into a machine event, and reports the machine’s verdict back. The agent can be “creative” with language (still working on this part, so YMMV here); the machine is authoritative on state.</p>
<h2 id="orchestration-machine-state-drives-the-agent">Orchestration: Machine State Drives the Agent</h2>
<p>This is where things get fun; dynamic tool swapping. The agent’s capabilities — both its instructions and its available tools — expand and contract based on the deterministic core’s state:</p>
<pre><code class="hljs language-typescript"><span class="hljs-comment">// Before verification: agent can ONLY verify</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> preVerificationTools = [
  <span class="hljs-title function_">mastraToolToOpenAI</span>(<span class="hljs-string">&quot;verify-identity&quot;</span>, verifyIdentityTool),
];

<span class="hljs-comment">// After verification: agent gets financial tools</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> postVerificationTools = [
  <span class="hljs-title function_">mastraToolToOpenAI</span>(<span class="hljs-string">&quot;verify-identity&quot;</span>, verifyIdentityTool),
  <span class="hljs-title function_">mastraToolToOpenAI</span>(<span class="hljs-string">&quot;getTransactions&quot;</span>, getTransactionsTool),
  <span class="hljs-title function_">mastraToolToOpenAI</span>(<span class="hljs-string">&quot;getAccounts&quot;</span>, getAccountsTool),
];
</code></pre>
<p>When the machine transitions to “verified”, the agent’s entire configuration gets swapped in Mastra — different instructions, different tools:</p>
<pre><code class="hljs language-typescript"><span class="hljs-comment">// Create session state machine and inject into agent&#x27;s runtime</span>
sessionActor = <span class="hljs-title function_">createSessionActor</span>();
<span class="hljs-keyword">const</span> runtimeContext = <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeContext</span>();
runtimeContext.<span class="hljs-title function_">set</span>(<span class="hljs-string">&quot;sessionActor&quot;</span>, sessionActor);

<span class="hljs-keyword">await</span> agent.<span class="hljs-property">voice</span>.<span class="hljs-title function_">connect</span>({ runtimeContext });

<span class="hljs-comment">// Start with limited tools and verification-focused instructions</span>
agent.<span class="hljs-property">voice</span>.<span class="hljs-title function_">updateConfig</span>({
  <span class="hljs-attr">instructions</span>: <span class="hljs-string">`You are a helpful financial assistant.
    The user has NOT been verified yet.
    Ask them to provide their 4-digit verification ID.
    Do NOT discuss any financial details until verified.`</span>,
  <span class="hljs-attr">tools</span>: preVerificationTools,
});

<span class="hljs-comment">// When the machine transitions to &quot;verified&quot;, unlock everything</span>
agent.<span class="hljs-property">voice</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">&quot;tool-call-result&quot;</span>, <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { toolName, result } = data;
  <span class="hljs-keyword">if</span> (toolName === <span class="hljs-string">&quot;verify-identity&quot;</span> &amp;&amp; result?.<span class="hljs-property">verified</span>) {
    agent.<span class="hljs-property">voice</span>.<span class="hljs-title function_">updateConfig</span>({
      <span class="hljs-attr">instructions</span>: <span class="hljs-string">`You are a helpful financial assistant.
        The user has been successfully verified.
        Use get-transactions and get-accounts to help them
        with their finances. Be conversational and helpful.`</span>,
      <span class="hljs-attr">tools</span>: postVerificationTools,
    });
  }
});
</code></pre>
<p>The machine transition (<code>greeting</code> → <code>verified</code>) is deterministic and testable. The <em>consequence</em> of that transition — swapping out the agent’s entire instruction set and available tools — is where the agentic shell gets reconfigured, and is also deterministic and testable.</p>
<h2 id="scaling-a-real-voice-workflow">Scaling: A Real Voice Workflow</h2>
<p>The code above is simple on purpose, but early tests and spikes have me confident that the pattern works. Imagine a production voice agent where callers dial in and speak to an AI assistant to complete a multi-step workflow over the phone:</p>
<p><a href="https://telnyx.com">Telnyx</a> handles telephony and streams audio over a WebSocket. <a href="https://mastra.ai">Mastra</a> connects to the <a href="https://developers.openai.com/api/docs/guides/voice-agents/">OpenAI Realtime API</a> for speech-to-speech — the caller speaks naturally and hears natural speech back. The round trip for a single turn looks like this:</p>
<pre><code><span class="hljs-number">1.</span> Caller speaks
   → Telnyx streams audio (μ-law, <span class="hljs-number">8</span>kHz) via WebSocket

<span class="hljs-number">2.</span> OpenAI Realtime processes speech
   → Mastra forwards audio <span class="hljs-keyword">to</span> OpenAI
   → Speech-<span class="hljs-keyword">to</span>-<span class="hljs-built_in">text</span> + LLM reasoning

<span class="hljs-number">3.</span> LLM calls a tool
   → take_action({ action: <span class="hljs-string">&quot;SUBMIT_ID&quot;</span>, params: { <span class="hljs-built_in">id</span>: <span class="hljs-string">&quot;123456&quot;</span> } })
   → Tool runs: machine.send({ type: <span class="hljs-string">&quot;SUBMIT_ID&quot;</span>, <span class="hljs-built_in">id</span>: <span class="hljs-string">&quot;123456&quot;</span> })
   → Machine transitions: greeting → verifying_identity
   → Tool returns: { success: <span class="hljs-literal">true</span>, nextStep: <span class="hljs-string">&quot;ask_for_details&quot;</span> }

<span class="hljs-number">4.</span> OpenAI generates response
   → Uses tool <span class="hljs-literal">result</span> <span class="hljs-keyword">to</span> generate spoken response
   → <span class="hljs-string">&quot;Thanks, I&#x27;ve got that. Now, could you tell me which
      account you&#x27;d like to work with?&quot;</span>

<span class="hljs-number">5.</span> Audio plays <span class="hljs-keyword">to</span> caller
   → Mastra → Hono API → WebSocket → Telnyx → phone
</code></pre>
<p>The agentic shell (Mastra + OpenAI) handles everything about the <em>conversation</em> — hearing the caller, interpreting speech, generating natural responses, handling “ums” and false starts and off-topic questions. Early signs are good, and one happy finding: the OpenAI realtime voice model supports interruptions and bi-directional voice communication out of the box.</p>
<p>The deterministic core (XState) handles everything about the <em>workflow</em> — what state we’re in, what’s valid, what happens next. The tools are the membrane between them.</p>
<p>Two tools do the heavy lifting: <code>get_current_state</code> lets the agent ask the machine “where are we and what can I do?”, and <code>take_action</code> lets the agent tell the machine “let’s move forward.” The machine enforces the rules — its guards reject anything that doesn’t satisfy the business logic. The agent is creative about <em>how</em> to have the conversation; the machine is authoritative about <em>what happens next</em>.</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> get_current_state = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;get_current_state&quot;</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">&quot;Get current state of the workflow for this session&quot;</span>,
  <span class="hljs-attr">handler</span>: <span class="hljs-keyword">async</span> ({ sessionId }) =&gt; {
    <span class="hljs-keyword">const</span> machine = <span class="hljs-title function_">getMachine</span>(sessionId);
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">state</span>: machine.<span class="hljs-property">state</span>.<span class="hljs-property">value</span>,
      <span class="hljs-attr">context</span>: machine.<span class="hljs-property">state</span>.<span class="hljs-property">context</span>,
      <span class="hljs-attr">availableActions</span>: machine.<span class="hljs-property">state</span>.<span class="hljs-property">nextEvents</span>,
    };
  },
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> take_action = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;take_action&quot;</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">&quot;Advance workflow by sending an event to machine&quot;</span>,
  <span class="hljs-attr">parameters</span>: {
    <span class="hljs-attr">action</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;string&quot;</span>,
      <span class="hljs-attr">enum</span>: [<span class="hljs-string">&quot;SUBMIT_ID&quot;</span>, <span class="hljs-string">&quot;SELECT_ACCOUNT&quot;</span>, <span class="hljs-string">&quot;CONFIRM&quot;</span>,
             <span class="hljs-string">&quot;ACCEPT_TERMS&quot;</span>, <span class="hljs-string">&quot;COMPLETE&quot;</span>],
    },
  },
  <span class="hljs-attr">handler</span>: <span class="hljs-keyword">async</span> ({ sessionId, action, params }) =&gt; {
    <span class="hljs-keyword">const</span> machine = <span class="hljs-title function_">getMachine</span>(sessionId);
    machine.<span class="hljs-title function_">send</span>({ <span class="hljs-attr">type</span>: action, ...params });
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">newState</span>: machine.<span class="hljs-property">state</span>.<span class="hljs-property">value</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-title function_">getStateMessage</span>(machine.<span class="hljs-property">state</span>.<span class="hljs-property">value</span>),
    };
  },
};
</code></pre>
<p>The system prompt constrains the agent’s conversational behavior, while the machine constrains the workflow:</p>
<pre><code class="hljs language-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-variable constant_">SYSTEM_PROMPT</span> = <span class="hljs-string">`
You are a professional assistant helping callers complete a workflow over the phone.

RULES:
1. Be clear and patient. Use simple language.
2. Always disclose important information upfront.
3. Never rush the caller. Frame every step as optional.
4. If you don&#x27;t understand, ask for clarification.
5. If something goes wrong, apologize and offer a retry or human transfer.

CONVERSATION FLOW:
1. Verify the caller&#x27;s identity
2. Present available options, caller selects
3. Run validation, explain if ineligible
4. Disclose any fees or terms, confirm acceptance
5. Execute the action, confirm completion

TOOLS:
- get_current_state: Check where we are in the workflow
- take_action: Advance the workflow
`</span>;
</code></pre>
<p>The system prompt also tells the agent <em>how</em> to talk; the machine tells it <em>when</em>. Language lives in the shell, logic lives in the core.</p>
<h2 id="boundaries">Boundaries</h2>
<p>Applying the core/shell lens has helped me think about where to draw the boundary between agent and state machine, and we are now in a world where it is trivial to produce working concepts that can validate where that line should be.</p>
<p>One way you might think about this is something like “let’s start with one Mastra tool, <code>getMachine</code>, and the only thing it was able to do was get the next state from the machine.” </p>
<p>This is a decent place to start, but kind of fell over in short order because there’s some amount of non-determinism you need in order for an agentic voice solution to work well — the agent needs to interpret messy human speech, handle interruptions, deal with off-topic questions, and generally be flexible about <em>how</em> it gets the information the machine needs.</p>
<p>What I’ve found works better is more guard-rails in the system prompt for the tools and workflow, but the principle of having the agent calling the machine to figure out what’s possible and constrain the states and tools that can be called next has proven extremely valuable. </p>
<p>The primary motivation should immediately be clear: <strong>get to determinism as fast as possible.</strong></p>
<p>The voice agent interprets the caller’s input (non-deterministic), figures out what they’re trying to do (non-deterministic), and then immediately hands off to the machine (deterministic). The machine decides what’s valid, what the next step is, and what tools become available. The agent gets back a structured result and turns it into natural speech (non-deterministic again).</p>
<p>The non-deterministic parts are <em>thin</em> — the deterministic core is <em>thick</em> and does the actual work.</p>
<h2 id="lineage">Lineage</h2>
<p>I fell into an obsession with state machines as an architectural pattern by accident. I got to learn from Shawn, Jason, and Kevin at Vendasta, who were drawing on the pioneers that came before them. And I suspect there are more through-lines worth tracing, by revisiting old whitepapers and watching how ideas get refined as they move from theory to practice.</p>
<table>
<thead>
<tr>
<th>Era</th>
<th>Tool</th>
<th>Pattern</th>
</tr>
</thead>
<tbody><tr>
<td>1955-56</td>
<td>Mealy &amp; Moore</td>
<td>original FSM models, “machines”</td>
</tr>
<tr>
<td>1999</td>
<td>van Gurp &amp; Bosch</td>
<td>config vs runtime split, solid architecture</td>
</tr>
<tr>
<td>2008-2011</td>
<td>Vendasta / Fantasm</td>
<td>YAML-configured FSMs on appengine taskqueues</td>
</tr>
<tr>
<td>2012</td>
<td>Gary Bernhardt</td>
<td>functional core, imperative shell</td>
</tr>
<tr>
<td>2020</td>
<td>SurveyMonkey POC</td>
<td>xstate machine defs generated from a GUI, serialized, run in browser</td>
</tr>
<tr>
<td>2025-26</td>
<td>XState + Mastra</td>
<td>deterministic core, agentic shell</td>
</tr>
</tbody></table>
<p>Every time I pick up a new codebase, I find the same thing hiding under the surface — a half-baked state machine that nobody drew on a whiteboard first. Events firing into the void, state scattered across a dozen files, transitions implicit in if/else chains or switch statements three levels deep.</p>
<p>If you believe the chatter online, then you might feel like we are dealing with a lot more non-determinism these days; the imperative I/O <a href="https://x.com/garybernhardt">Gary</a> uses to frame his version of the shell definitely feels a lot more deterministic to me than an LLM that can hallucinate, go off-script, or confidently tell a caller something that isn’t true. </p>
<p>That makes the case for a deterministic core <strong>a lot stronger</strong>.</p>
]]></description><link>https://blog.davemo.com/posts/2026-02-14-deterministic-core-agentic-shell.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2026-02-14-deterministic-core-agentic-shell.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate></item><item><title><![CDATA[Vibing Like It's 1999]]></title><description><![CDATA[<aside class="tldr">LLMs make great "digital archivists," resurrecting old sites and finding lost assets via the Wayback Machine.</aside>

<p>I’ve been down a nostalgia rabbit hole lately, trying to track down a lot of my early internet artifacts—including my first website.</p>
<p>I had been hunting in the various internet archives for <em>DM Designs™</em>, the first website I published in 1998, but for the life of me I couldn’t remember the URL slug. I knew the <a href="https://en.wikipedia.org/wiki/GeoCities">GeoCities</a> neighborhood was in Sunset Strip, but my searches in <a href="https://blog.archive.org/2009/08/25/geocities-preserved/">many</a> of the <a href="https://www.oocities.org/">remaining</a> <a href="https://geocities.restorativland.org/">archives</a> always came up empty. Those archives are themselves admittedly only partial snapshots of what once was.</p>
<p>As luck would have it, while digging around in my basement, I stumbled across a DVD-RW I had burned around 2003. It was a backup of many of my early web projects, and stuffed in a folder was a partial copy of the GeoCities site.</p>
<p>The code was a typical late ‘90s mess: invalid HTML, broken images, the works. Pretty common for the time, given that a lot of it was built with the GeoCities WYSIWYG editor. This was my very first foray into web development. Finding the site was great, but getting it up and running again turned into a fun experiment that became its own side-quest exploring what features existed in web browsers at the time.</p>
<p>Reflecting on this almost 30 years later, I’m struck by how robust HTML, CSS, and JavaScript are. It’s remarkable how well these languages and browsers have held up over time.</p>
<h2 id="the-hunt-for-missing-pieces">The hunt for missing pieces</h2>
<p>I started exploring whether some of the assets missing from my local archive could be recovered from the <a href="https://web.archive.org/">Wayback Machine</a>. There were a few hits for some of my other sites, but for this one a lot was missing. I still couldn’t find the original snapshot of my page either. It figures. A 17-year-old kid’s personal design page probably wasn’t notable enough for the Wayback Machine to have crawled, or for any of the GeoCities archives to have captured.</p>
<p>I’ve been using agentic coding tools like Claude and Codex regularly, and I often default to them for brainstorming sessions. Through the process of trying to resurrect the site, I discovered that they are surprisingly capable as partners in digital archival searches through the Wayback Machine. Early explorations helped me understand the Wayback Machine’s free <a href="https://archive.org/help/wayback_api.php">JSON API</a>. I could tweak URL parameters and use regex matching to quickly search for assets or even partial URL slugs. Using that, I was able to recover some of the things I was missing.</p>
<h2 id="the-stories-geocities-markup-tell">The stories GeoCities markup tell</h2>
<p>The first thing I noticed when opening the site’s index page was actually the name of the file itself: <code>index.htm</code>. Opening it in <a href="https://zed.dev/">Zed</a> threw a UTF-8 error: <code>stream did not contain valid UTF-8</code>, which jogged my memory about early web browsers and encodings that may have predated UTF-8. The encoding markup in the HTML showed up as a Latin encoding, which surprised me, but the <code>meta</code> tags in the markup told more of the story:</p>
<pre><code class="hljs language-html">&lt;META <span class="hljs-attribute">HTTP-EQUIV</span>=<span class="hljs-string">&quot;Content-Type&quot;</span> <span class="hljs-attribute">CONTENT</span>=<span class="hljs-string">&quot;text/html; charset=iso-8859-1&quot;</span>&gt;
&lt;META <span class="hljs-attribute">NAME</span>=<span class="hljs-string">&quot;GENERATOR&quot;</span> <span class="hljs-attribute">CONTENT</span>=<span class="hljs-string">&quot;Mozilla/4.04 [en] (Win95; I) [Netscape]&quot;</span>&gt;
</code></pre>
<p>The <code>GENERATOR</code> tag shows I was using Netscape Communicator 4.04, which at the time included Netscape Composer, a WYSIWYG HTML editor. <code>[en]</code> is for English.</p>
<p>Claude told me that in the late ‘90s, the Windows 95 English version I was using likely would have used <em>Windows-1252</em> (Western European) as its default, and <em>Netscape Composer</em> defaulted to <em>ISO-8859-1</em> for HTML files when set to the English locale. Interestingly, these two encodings are nearly identical and only differ in a few code points—but I knew none of that back in 1998.</p>
<p>As I explored the markup, I made a happy discovery: I was able to confirm the original GeoCities slug and neighborhood my page was part of, <a href="http://geocities.com/SunsetStrip/Palladium/4011">http://geocities.com/SunsetStrip/Palladium/4011</a>. Alas, I still can’t seem to find an original copy in any of the online archives. GeoCities was <em>huge</em> though, and it seems like preservation efforts probably started too late for just how big that part of the internet was at the time.</p>
<p>My archive had a mix of 3-character and 4-character filename extensions for the markup itself—<code>index.htm</code> and <code>contactpage.html</code>. I was wondering why and did some digging. Claude told me this was another artifact of DOS <code>v8.3</code> filename conventions, which had clearly influenced early web development. Part of me vaguely recalls Microsoft FrontPage 98 being fond of using 3 character extensions.</p>
<p>Anyway, I updated all the markup files to use a <code>.html</code> extension and updated all my internal links accordingly.</p>
<h2 id="claude-as-digital-archivist">Claude as digital archivist</h2>
<p>I ended up leveraging Claude as a kind of digital archivist to help me fix everything that was wrong with the site, but I instructed it to take extra care not to fundamentally change anything. We fixed the broken encoding, standardized filename extensions, dealt with missing images, cleaned up invalid characters that appeared when I converted to <code>UTF-8</code>, and added missing closing tags so my liberal use of <code>&lt;CENTER&gt;</code>, <code>&lt;FONT&gt;</code>, and <code>&lt;TABLE&gt;</code> would render properly again.</p>
<p>I had assumed this site predated the W3C validator, but a quick search showed it had just launched in late 1997. It clearly wasn’t on my radar at all, as I recall becoming much more concerned with validation only once <a href="https://en.wikipedia.org/wiki/XHTML">XHTML and an official validator</a> launched a few years later in 2000.</p>
<p>It was a similar story for CSS: the frequent use of stylistic attributes like <code>border</code>, <code>bgcolor</code>, <code>height</code>, and <code>width</code> at the time had me wondering if the site predated CSS, but a cursory glance at the W3C spec told me I just didn’t know about it yet and leaned heavily on the tools in my toolbox and what all the other codegen tools like GeoCities were doing at the time: inline styles, tables for layout, and gratuitous use of animated <code>.gif</code> files!</p>
<p>A whole bunch of JavaScript was still there trying to load and execute, and the popups still fired!</p>
<pre><code class="hljs language-html"><span class="hljs-tag">&lt;<span class="hljs-name">SCRIPT</span> <span class="hljs-attr">LANGUAGE</span>=<span class="hljs-string">&quot;javascript&quot;</span>&gt;</span><span class="language-javascript">
<span class="hljs-keyword">var</span> cuid= <span class="hljs-string">&quot;10199&quot;</span>; <span class="hljs-keyword">var</span> keywords= <span class="hljs-string">&quot;none&quot;</span>;
<span class="hljs-keyword">var</span> urlOfNewPop=<span class="hljs-string">&quot;http://www.geocities.com/ad_container/pop.html?cuid=&quot;</span>+cuid+<span class="hljs-string">&quot;&amp;keywords=&quot;</span>+keywords; oldPop=<span class="hljs-variable language_">window</span>.<span class="hljs-title function_">open</span>(urlOfNewPop, <span class="hljs-string">&#x27;_popIt&#x27;</span>, <span class="hljs-string">&#x27;width=515,height=125&#x27;</span>);

<span class="hljs-keyword">if</span> (oldPop.<span class="hljs-property">location</span>.<span class="hljs-property">href</span> != urlOfNewPop) {
  <span class="hljs-keyword">if</span> ((navigator.<span class="hljs-property">appName</span> == <span class="hljs-string">&quot;Netscape&quot;</span>) &amp;&amp; (<span class="hljs-built_in">parseInt</span>(navigator.<span class="hljs-property">appVersion</span>) == <span class="hljs-number">3</span>)) {
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-string">&quot;oldPop.close()&quot;</span>, <span class="hljs-number">750</span>);
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-string">&quot;window.open(urlOfNewPop, &#x27;_popIt&#x27;, &#x27;width=515,height=125&#x27;)&quot;</span>, <span class="hljs-number">1700</span>);
  } <span class="hljs-keyword">else</span> {
    oldPop.<span class="hljs-title function_">close</span>();
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-string">&quot;window.open(urlOfNewPop, &#x27;_popIt&#x27;, &#x27;width=515,height=125&#x27;)&quot;</span>, <span class="hljs-number">1000</span>);
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">SCRIPT</span>&gt;</span>
</code></pre>
<blockquote>
<p>The infamous GeoCities popup JavaScript, which still works if you allow popups.</p>
</blockquote>
<h2 id="as-was-the-fashion-at-the-time">As was the fashion at the time</h2>
<p>Digging into some of the image files was illuminating. I discovered all kinds of format “carbon-dating” clues in the metadata: image dimensions and even which tools were used to create them! This was very helpful, because nearly all of the images in the markup had dimension attributes, and it was useful to correlate which ones matched the files on disk—many of which had different names for some reason. Perhaps an artifact of when I had uploaded them to GeoCities? I have very vague memories of using a web editor for pasting snippets of HTML and uploading images. God, I feel old.</p>
<p>Most of the JPEG files were <code>.jpg</code> and contained JFIF headers (JPEG File Interchange Format) at 72 DPI resolution—pretty much a standard for web graphics at the time. GIF files were version <code>89a</code>, one of the first versions that supported transparency and animation. The animations were built with <a href="https://www.mindworkshop.com/gifcon.html">GIF Construction Set</a>, and a lot of the artwork was done with Photoshop 4.0.1.</p>
<aside>I was most definitely a "lens flare" and "difference clouds" enjoyer.</aside>

<h2 id="an-exploration-partner">An exploration partner</h2>
<p>This whole exploration was a lot of fun. I don’t know about you, but looking back that far into the past definitely brought me right back to where I was as a 17-year-old working on the core pieces of the web and not knowing a damn thing about it.</p>
<p>It was fun to have an LLM partner throughout my explorations too. I think there’s a lot of emphasis on using AI for generating new content, but the more I use these tools and default to them, the more I find them to be really powerful partners for exploration and, in this case, digital archaeology. They are able to recall a lot of information about obscure old tech, which isn’t surprising—they were trained on a lot of the public internet. The fact that they can quickly reference spec files, jump back in time, look at and understand deprecated APIs and historical file formats, read 25-year-old code, understand the intent with enough context, and help me fix it—that’s pretty cool.</p>
<p>This also got me thinking about other ways to use this capability. One of the other things I discovered on my backup disk was a lot of early internet art I had created—3D animations from a pirated copy of 3D Studio Max, an old hand-rotoscoped lightsaber test video, and even an old Half-Life 2 steam skin that crashed my web host in the early 2000’s. Those are stories for another time. But one of the things I’m interested in exploring next is maybe building a Claude Skill to time-warp back to the nineties—to impart enough knowledge in the skill definition that Claude could build a new site, but with all the constraints, techniques, and style of the time.</p>
<p>If nothing else, this convinced me that LLMs aren’t just for generating new stuff—they’re weirdly good at helping us dig up, understand, and preserve the old, broken, wonderful corners of the web too.</p>
<p>For now, I’m pretty happy that I found my backup archive and that I was able to restore my site. Here it is in all its glory: <a href="https://1998.davemo.com">1998.davemo.com</a></p>
]]></description><link>https://blog.davemo.com/posts/2025-11-13-vibing-like-its-1999.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2025-11-13-vibing-like-its-1999.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate></item><item><title><![CDATA[Sora 2 for Lookdev]]></title><description><![CDATA[<aside class="tldr">
Sora is a surprisingly capable idea generator for gamedev pre-production.
</aside>

<p>I spent a few hours yesterday experimenting with Sora 2, trying to figure out if it could help with asset pre-production for a game idea I’ve been noodling on for about a year. Turns out, it’s a pretty capable partner—with caveats. You can get solid consistency in <em>format</em> (camera angles, poses, framing) by being specific in your prompts, but aesthetic consistency—the actual look and feel across generations—is still a crapshoot.</p>
<p>The levers for locking down visual style just aren’t there yet, or at least not exposed. Still, for pre-production exploration and prototyping? It’s pretty damn useful if you set the right expectations.</p>
<h2 id="my-gamedev-origins">My GameDev Origins</h2>
<p>Before I became a full-time programmer, I was into 3D graphics and graphic design. We got our first family computer in 1996—an Acer Aspire with a knockoff Cyrix 6x86-P133 chip. I cut my teeth on pirated copies of Photoshop, Illustrator, and 3D Studio Max—downloaded over dialup—diving deep into the world of 3D modeling, texturing, and animation. I never quite mastered rigging (inverse kinematics confused the hell out of me), but I was obsessed.</p>
<p><a href="https://www.blender.org/">Blender</a> first got on my radar around this time, back when it was freeware, pre-OSS release (I think I started around version <code>1.6</code>). I loved working in Blender because it was small, capable, and I didn’t have to sail the high seas. 🏴‍☠️</p>
<p>As I got deeper into 3D work, I naturally gravitated toward level design—first with <a href="https://en.wikipedia.org/wiki/Build_(game_engine">Ken Silverman’s Build Engine</a> (Duke Nukem 3D), then later with Valve’s <a href="https://en.wikipedia.org/wiki/GoldSrc#:~:text=Worldcraft">Worldcraft</a> (Half-Life). There was something satisfying about building playable spaces, not just rendering static scenes. That’s what eventually pulled me into the world of game modding.</p>
<p>Fast forward to 2003: <a href="https://en.wikipedia.org/wiki/S2_Games">S2 Games</a> had just released <a href="https://en.wikipedia.org/wiki/Savage:_The_Battle_for_Newerth">Savage: The Battle for Newerth</a>. I met a team of devs on Freenode who were building a mod on top of the Silverback engine, and I dipped my toes into the gamedev industry properly for the first time.</p>
<p>The mod didn’t really go anywhere—I can’t even remember the name of it—but I found myself far more interested in the web dev side and poured my energy into building our little modding team’s web presence and styling the <a href="https://www.phpbb.com">phpBB</a> installation we had going to manage development. Still, that experience gave me a taste of the gamedev pipeline: concepting, pre-production, asset production, prototyping, playtests. Some 25 years later, that foundational knowledge is proving useful as I re-engage with gamedev.</p>
<h2 id="the-game-idea">The Game Idea</h2>
<p>The concept is simple: a dwarven-themed take on the excellent <a href="https://moonlighterthegame.com">Moonlighter</a>, with a twist. Instead of being a shopkeeper, you’re an innkeeper. You run an inn during the day—making food, breaking up bar fights between dwarven patrons, earning coin to invest in better furnishings. Instead of “moonlighting” in dungeons after hours, you go hunting, gathering, fishing, and foraging for higher-quality ingredients to feed your ever-hungry customers. Part sim, part survival-lite.</p>
<p>I have some lofty goals for multiple gameplay loops, but I’ve set those aside to just start prototyping and producing assets. That’s where Sora 2 comes in.</p>
<h2 id="lookdev-and-ai">Lookdev and AI</h2>
<p>Today, we have these incredibly powerful idea engines that let us produce assets in minutes—assets that would’ve taken days for a single artist just a few years ago. Sure, they’re low quality and better suited for prototyping right now, but the speed is transformative.</p>
<p>I started with a basic question: <strong>What type of prompt would I need to get Sora to produce lookdev assets for a dwarven innkeeper in a neutral pose?</strong></p>
<h2 id="early-experiments-finding-the-aesthetic">Early Experiments: Finding the Aesthetic</h2>
<p>My early prompts explored stylistic elements—texture, theme, stylized vs. photorealistic. I ping-ponged between Sora and GPT-5, using the latter to educate me on formal terminology I’d forgotten or never learned.</p>
<p>Here are some early attempts:</p>
<blockquote>
<p>2.35:1 cinematic, 24fps. Opening wide shot (24mm) eye-level, slow push-in through bustling dwarven taproom with flagstones, oak beams, rune-carved pillars. Warm 3000K firelight + practical candles, soft rim lighting; gentle haze for light rays. Cut to medium (50mm) tracking left as innkeeper (braided beard, leather apron) slides tankard; shallow DOF. High-detail props: copper taps, carved mugs, stew steam. Final close-up (85mm) with rack focus at 8s from stew to ledger UI: minimalist diegetic overlay (gold coins + rep meter). Teal-orange grade, subtle 35mm film grain, Deakins-style naturalism. Natural motion blur, clean cut transitions.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_style_exploration_1-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_1.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_1.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<blockquote>
<p>One unbroken 10s take, 60fps, real-time gameplay capture. Low-poly dwarven tavern with toon outline shader and flat color ramps. Third-person shoulder cam follows the innkeeper from keg to bar; smooth, game-like acceleration/deceleration. Patrons in looping emotes (cheer, sit, wave). Lighting: high-key warm directional + baked bounce; crisp shadows. Foam as simple particle sprites; stew steam as billboard particles. HUD: thin outlined panels with bold pictograms; +10 coins tick at top-left, Order Served toast top-right. No cuts, no photo montage, no still frames. Loop seamlessly.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_style_exploration_2-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_2.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_2.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<blockquote>
<p>Real-time gameplay capture, continuous 10s, 60fps, no cuts. Third-person follow cam tracking a dwarven innkeeper jogging through misty conifer forest at blue hour; hand-painted terrain, stylized-PBR foliage; volumetric fog and light shafts. Subtle head-bob, camera eases around trees; IK foot planting. The character harvests glowing chanterelles (bioluminescent), +1 ingredient appears as tiny rune HUD near the mushrooms, then fades. Audio: soft wind, branch creaks, cloth rustle. Cool teal environment vs warm fungus glow; mild DOF; foamless motion blur. Seamless loop.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_style_exploration_3-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_3.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_style_exploration_3.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<p>These experiments were fun, but not exactly what I was looking for. They helped me identify what I liked aesthetically and gave me a sense of Sora’s capabilities. As I refined my prompts, I realized there was likely a more structured approach that would help with look tests of characters, props, and animations.</p>
<h2 id="the-what-ifs">The “What Ifs”</h2>
<p>Like any good coding rabbit hole, my journey had me wondering:</p>
<ul>
<li>What if I could get consistent character outputs from Sora?</li>
<li>What if I could use those to generate A-pose and other lookdev assets that a traditional 3D artist might use?</li>
<li>What if I could take that asset and auto-generate a rough pre-production mesh?</li>
<li>What if there existed a tool that could auto-edit and auto-rig the model so I could drop it into a test level?</li>
</ul>
<p>I didn’t get to that last one (yet!), but I did accomplish the first three. And honestly? It’s really damn cool. We’re living in the most amazing version of “draw the rest of the owl” I ever imagined possible, and if you have any creativity whatsoever and a vision for what good looks like, it’s such an energizing time to be a builder of things.</p>
<h2 id="constraining-sora-to-give-consistent-lookdev-outputs">Constraining Sora to Give Consistent LookDev Outputs</h2>
<p>Once I realized you could constrain Sora with detailed prompts—including key lighting, temperature, intensity, materials, specific geometry, animation loops—I started experimenting more deliberately.</p>
<p>The thing is, I’d been away from game production for about twenty-five years, and the industry has evolved <em>massively</em> in that time. Back when I was doing this stuff, I was entirely self-taught, scraping together knowledge from forums and tutorials. There’s a whole professional vocabulary and standardized pipeline terminology now that didn’t really exist—or at least wasn’t codified—when I was working in 3D Studio Max and early Blender. I just didn’t know what I didn’t know.</p>
<p>So I leaned on GPT-5 as an educator. I found that shifting my role to that of a learner, and being specific about my background, was surprisingly effective context engineering. I told GPT-5 about my gamedev history, and it would respond with, “Ah yes, these are the pieces of the pipeline you’re describing, and here’s the modern terminology and keywords—here’s how to put it together in a prompt for Sora.”</p>
<p>The challenge was getting stylistic consistency. I went through many iterations, some decent but not quite the aesthetic I wanted. When I found something I liked, I doubled down on it, tweaking prompts to try and maintain a common theme.</p>
<p>The key insight here: <em>specificity breeds consistency</em>. The more detailed your prompt about lighting, materials, pose, and camera behavior, the more control you have over the output. When you don’t know what you don’t know, you can ask GPT-5 or another LLM to help bridge that gap, translating your intent into the precise terminology that makes prompts more effective.</p>
<h2 id="example-prompts-that-worked">Example Prompts That Worked</h2>
<blockquote>
<p>Real-time character lookdev test, one continuous take, 10 seconds, 60fps, no cuts, no still frames, no timelapse. Locked front orthographic camera, waist-up to full-body framing, centered. Painted-toon hybrid: low/medium-poly forms, hand-painted albedo with visible brush strokes, stylized-PBR lighting, thin 1–2px toon outlines, two-step shadow ramps (teal shadows, warm highlights). Dwarven innkeeper: braided beard, leather apron, oak-tone bracers, stout silhouette. Idle animation: slow breathing, slight weight shift, tiny head turn, natural blinks; beard/cloth secondary motion; subtle finger fidgets. Lighting: soft key 3000–3500K from screen-left, rim from screen-right 5000K, gentle bounce; neutral backdrop (oak-grain or mid-gray). No camera move, no DOF, natural motion blur minimal. Turn on shader cues: specular on brass buckles, matte leather roughness, painted wood grain. Loop seamlessly end-to-start.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_consistency_exploration_1-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_1.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_1.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<blockquote>
<p>Real-time character turnaround test, continuous single take, 10s, 60fps. Locked camera, character rotates 360° clockwise at constant speed on a turntable; no cuts. Painted-toon hybrid look: hand-painted textures, thin toon outlines, two-step shadow ramps, stylized-PBR highlights on brass fittings. Lighting: three-point—key 3200K, fill −1.5 EV, cool rim 5500K; floor card for bounce. Animation: subtle idle breathing while rotating; beard/cloth secondary motion; no exaggerated sway. Backdrop: neutral studio sweep; no DOF. Material callouts visible: leather roughness, wood grain on tankard at belt, brushed metal on belt hooks. Seamless loop end-to-start (rotation lands on front exactly at 10s).</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_consistency_exploration_2-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_2.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_2.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<blockquote>
<p>Real-time lookdev test, one continuous take, 10 seconds, 60fps, no cuts, no still frames, no timelapse, seamless loop end-to-start. Painted-toon hybrid: low/medium-poly forms, hand-painted albedo with visible brush strokes, stylized-PBR lighting, thin 1–2px toon outlines, two-step shadow ramps (teal shadows, warm highlights). Locked front orthographic camera, full-body, centered trio. Neutral mid-gray studio backdrop, no DOF, natural motion blur minimal. Three dwarven innkeeper variants stand side-by-side at equal scale: Tankard-Bearer (broad shoulders), Smith-Chef (apron + skillet on belt), Scout (lighter kit). All perform subtle idle: slow breathing, slight weight shift, natural blinks; beards/cloth secondary motion. High-contrast rim so silhouettes read clearly; lighting constant; no camera move.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_consistency_exploration_3-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_3.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_consistency_exploration_3.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<h2 id="getting-sora-to-build-useful-a-pose-assets">Getting Sora to Build Useful A-Pose Assets</h2>
<p>Early on, I had pulled some Sora outputs into a demo version of Stable Fast 3D, then into Blender, but I ran into a few quality problems with the models. GPT-5 suggested a pre-production cleanup process I could do manually, but it felt tedious and I was really trying to optimize for speed over quality at this stage anyway.</p>
<p>I hadn’t considered this ahead of time, and my early assets didn’t have enough space between the arms, causing overlapping geometry that would make rigging difficult. So I went back to GPT-5 and learned about <a href="https://blender.stackexchange.com/questions/242252/a-pose-or-t-pose">A-poses</a>—neutral poses with arms at about 30-45 degrees from the body, rather than straight out (T-pose). This makes it easier for auto-rigging tools to identify limbs.</p>
<p>The frustrating part: as I refined the prompts for better poses, the aesthetic would sometimes shift. And despite my best attempts, Sora often wouldn’t complete a full 360° rotation within its 10-second limit.</p>
<h2 id="a-happy-accident--character-sheet-plates">A Happy Accident : Character Sheet Plates</h2>
<p>I was trying to get a character turnaround and accidentally left in all of the parameters from a prompt that GPT had told me to use one at a time for separate Sora iterations; this ended up causing  Sora to create a character sheet with multiple views. Instead of animating it, Sora generated a static image with front, right-profile, and back views—all on one plate. This turned out to be incredibly useful as a reference to snap screenshots from to use further on down the line for mesh generation.</p>
<blockquote>
<p>Character sheet plate, single still. Dwarven innkeeper, painted-toon hybrid (hand-painted albedo, thin 1–2px outlines, two-step ramps, stylized-PBR highlights kept subtle). Stocky heroic proportions (broad shoulders, short legs), braided beard with clear air-gap from chest, bald crown with close-cropped sides, simple tunic, belt, pants, boots; no props/cape/apron/toolkit. A-pose: arms ~30° down from horizontal, hands open, fingers separated; legs shoulder-width, feet flat on ground plane, head level, neutral face. Camera: orthographic, full-body, perfectly [FRONT / RIGHT-PROFILE / BACK] facing, centered; framing consistent. Background: neutral mid-gray seamless; no DOF, no motion blur. Lighting: even studio three-point (key 5000K softbox, fill −1 EV, soft rim); shadows soft and minimal; materials unchanged. Exposure even, no vignetting, no color cast. Style anchors: warm, hand-painted base colors (muted earth palette: umber leather, slate cloth, iron accents), clean silhouettes, no grunge overlays, readable edge accents on folds and seams. Negative: no pose changes, no stepping, no crossed limbs, no props, no background objects, no text, no logos.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_character_sheet_1-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_character_sheet_1.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_character_sheet_1.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<h2 id="other-a-pose-prompts-that-worked">Other A-Pose Prompts That Worked</h2>
<blockquote>
<p>Character turnaround, 10s, 60fps, single continuous take, loopable. Painted-toon hybrid (hand-painted albedo, thin 1–2px outlines, two-step ramps, stylized-PBR). Locked orthographic camera, full-body, centered; neutral mid-gray cyc; no DOF, no motion blur. Dwarven innkeeper in A-pose (arms ~30° down from horizontal), legs shoulder-width, feet flat, hands open with fingers separated; head level; neutral face. No props, no cape, no apron, no toolkit. Beard volume clearly separated from chest. Elbows and hands fully visible (no self-occlusion). Character rotates 360° clockwise at constant speed on a turntable; framing constant. Lighting: neutral studio 3-point (key 5000K softbox, fill −1 EV, subtle rim), soft shadows; materials unchanged throughout.</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_clean_a_pose_turnaround-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_clean_a_pose_turnaround.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_clean_a_pose_turnaround.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<p>I’ve since learned that the best angle for these lookdev assets is a 3/4 view—slightly offset (about 30 degrees) from the front—so you can see depth in the character model.</p>
<h2 id="generating-pre-production-meshes-with-stable-fast-3d">Generating Pre-Production Meshes with Stable Fast 3D</h2>
<p>Once Sora was producing reasonably consistent assets, I wanted to convert them into 3D meshes I could use in Blender.</p>
<p>Enter <a href="https://stable-fast-3d.github.io">Stable Fast 3D</a> from Stability AI. You can use their <a href="https://huggingface.co/spaces/stabilityai/stable-fast-3d">demo app on Hugging Face</a> if you don’t want to run anything locally, which is a pretty reasonable way to get pre-production meshes into your pipeline. But it runs pretty quick if you take the time to set it up—even on a Mac. I have an M3 Max with 36GB of RAM, and generating a mesh with 8,000–12,000 polys takes about 20 to 30 seconds.</p>
<pre><code class="hljs language-shell">~/code/innkeeper/stable-fast-3d git:(main)

PYTORCH_ENABLE_MPS_FALLBACK=1 uv run python run.py ./innkeeper_three_quarter.png --output-dir output/tris --remesh_option triangle --texture-resolution 2048
Device used:  mps
After Remesh 7309 14614
Peak Memory: 11525.65625 MB
<span class="hljs-meta">100%</span><span class="language-bash">|█████████████████████████████████████| 1/1 [00:29&lt;00:00, 29.03s/it]</span>
</code></pre>
<blockquote>
<p>I followed the instructions on the <a href="https://stable-fast-3d.github.io">Stable Fast 3D Repo</a> to enable <a href="https://github.com/Stability-AI/stable-fast-3d?tab=readme-ov-file#support-for-mps-for-mac-silicon-experimental">MPS support</a> on my Macbook, and installed pre-requisites using <a href="https://docs.astral.sh/uv/">uv</a>. (You may need to install torch like this: <code>uv pip install torch --no-build-isolation</code>).</p>
</blockquote>
<h2 id="the-final-context">The Final Context</h2>
<p>After many iterations, I had evolved my prompt to produce an asset with:</p>
<ul>
<li>Consistent character dimensions</li>
<li>A proper A-pose</li>
<li>Sequential camera cuts showing front, right-profile, back, and 3/4 views</li>
<li>Proper framing (no clipping of hands, feet, or elbows)</li>
</ul>
<blockquote>
<p>Character sheet, 10s, 60fps, single take, sequential camera cuts; no motion blur, no DOF. Dwarven innkeeper, painted-toon hybrid (hand-painted albedo, thin 1–2px outlines, two-step ramps, subtle stylized-PBR). A-pose: arms ~30° from horizontal, hands open, fingers separated; legs shoulder-width, feet flat; head level, neutral face; beard with visible air-gap from chest. Camera &amp; framing: orthographic, full-body, subject centered; lock zoom/orthographic size; character occupies ~70–80% of frame height; minimum 10% margin to all edges; no cropping of hands, feet, or elbows at any time; ground plane visible. Aspect 16:9. Background: neutral mid-gray seamless. Lighting constant (even studio three-point: key 5000K, fill −1 EV, soft rim). Timing: 0.0–2.5s = FRONT; 2.5–5.0s = RIGHT-PROFILE (90°); 5.0–7.5s = BACK (180°); 7.5–10.0s = 3⁄4 front-right (≈45°); loop resets to FRONT at 10s. Negative: no props/cape/apron/toolkit; no idle sway; no background objects; no text/watermarks;</p>
</blockquote>
<figure class="featurette">
  <video controls muted playsinline preload="metadata" poster="/img/sora-for-gamedev/lookdev_character_sheet_sequential-poster.jpg">
    <source src="/img/sora-for-gamedev/lookdev_character_sheet_sequential.mp4" type="video/mp4">
    <source src="/img/sora-for-gamedev/lookdev_character_sheet_sequential.webm" type="video/webm">
    Your browser does not support the <code>video</code> tag.
  </video>
</figure>

<p>The output <em>format</em> has been remarkably consistent, even if the <em>aesthetics</em> tend to drift between generations. For prototyping, that’s actually fine—I care more about format consistency than aesthetic consistency at this stage. What matters is being able to reliably screenshot these outputs, feed them into Stable Fast 3D, and get usable <code>.glb</code> meshes for rapid prototyping. The art style exploration is part of the process; having predictable poses and framing is what makes the workflow viable.</p>
<h2 id="automating-the-pipeline">Automating the pipeline</h2>
<p>I’ve always gravitated toward building internal tools in my software development career—there’s something deeply satisfying about being a toolmaker. So I asked myself: could I build a small web app that would:</p>
<ol>
<li>Take an image via command line</li>
<li>Automatically generate a mesh</li>
<li>Preview multiple quality variants in a web browser</li>
</ol>
<p>Turns out, you can. I mean, <em>of course</em> you can; we’re living in a world where tools you can imagine being useful are just a prompt away from existing, if you’re a sufficiently skilled context-smith.</p>
<p>I had GPT-5 and Codex whip up a python script that generates a webpage using <a href="https://modelviewer.dev">Google Model Viewer</a> to preview the <code>.glb</code> meshes that Stable Fast 3D generates (along with basic texture and lighting info). This script lets me batch-create models at varying quality levels and compare them instantly in a browser.</p>
<img src="/img/sora-for-gamedev/image_viewer_optimized.png" />
<img src="/img/sora-for-gamedev/blender_meshes_optimized.png" />

<h2 id="what-ive-learned">What I’ve learned</h2>
<p>Here are some key takeaways from this experiment I wanted to leave you with:</p>
<ol>
<li><p><strong>Specificity is everything</strong>. The more detailed your prompt—lighting temperature, material roughness, camera behavior, pose angles—the more consistent your outputs.</p>
</li>
<li><p><strong>Embrace happy accidents</strong>. The character sheet plate was a mistake, but it became one of the most useful outputs and guided me further down the rabbit hole.</p>
</li>
<li><p><strong>Use AI as an educator</strong>. Shifting to a “learner” role with GPT-5 and providing context about my background made the guidance far more relevant and actionable.</p>
</li>
<li><p><strong>Prototype fast, iterate faster</strong>. Even with limitations (10-second clips, inconsistent rotations), you can generate enough useful assets in a few hours to kickstart pre-production.</p>
</li>
<li><p><strong>Build tools to reduce friction</strong>. Automating the image-to-mesh-to-preview pipeline makes experimentation far more enjoyable, and it took like 15 minutes of back and forth with GPT-5.</p>
</li>
</ol>
<p>The barrier to entry keeps dropping, and I’m curious how far this type of workflow can scale.</p>
<h2 id="whats-next">What’s Next?</h2>
<p>I still want to explore:</p>
<ul>
<li>Auto-rigging pipelines or Blender add-ons</li>
<li>Using these meshes in <a href="https://godotengine.org">Godot</a> or <a href="https://www.babylonjs.com">Babylon.js</a> for actual gameplay prototyping</li>
<li>Experimenting with environment and prop generation</li>
</ul>
<p>If you’re interested in trying this workflow yourself, everything lives in <a href="https://github.com/davemo/gamedev-preproduction-pipeline">davemo/gamedev-preproduction-pipeline</a>. Feel free to <a href="https://twitter.com/dmosher">reach out</a> if you want to chat about gamedev pipelines—I’m always happy to trade notes.</p>
]]></description><link>https://blog.davemo.com/posts/2025-10-05-sora-for-gamedev-preproduction.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2025-10-05-sora-for-gamedev-preproduction.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate></item><item><title><![CDATA[Sora 2 Bugs]]></title><description><![CDATA[<aside class="tldr">
Post-generation checks in Sora 2 flagged my remix after an unexpected voice segment appeared.
</aside>

<p>I’ve been having an absolute blast with the recently released <a href="https://openai.com/index/sora-2/">Sora 2</a>, pushing its limits, testing edge cases, and generally seeing what kind of creative chaos I can conjure.</p>
<p>Yesterday, I had a fun idea to create a spoof video of <a href="https://justin.searls.co/">@searls</a> and myself presenting at a fictitious conference. You know, the kind of thing that would get a chuckle at the next tech meetup.</p>
<h2 id="the-future-of-js">The Future of “JS”</h2>
<blockquote>
<p>Original Prompt: dmosher and searls are panelists at a tech conference on the future of JS. Searls thinks the conference is about him because his initials are JS but it’s actually about the future of JavaScript.</p>
</blockquote>
<iframe
  width="315"
  height="560"
  src="https://www.youtube.com/embed/jFnDRWzXcqk"
  title="YouTube video player"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
  allowfullscreen
  style="display: block; margin: 20px auto;">
</iframe>

<p>It turned out better than I expected! I just wish they would have let the sentence continue so Justin could get to the punch line about getting to his autobiography, alas… perhaps in the next remix. 😂</p>
<h2 id="a-quick-primer-on-soras-onboarding">A Quick Primer on Sora’s Onboarding</h2>
<p>For those who haven’t been through it yet, when you onboard to Sora, it has you perform a rather sci-fi ritual. You read a sequence of numbers out loud while it captures an audio fingerprint—essentially training the model to understand the unique characteristics of your voice so it can synthesize it in generated videos. It does the same dance with your face, capturing a “likeness fingerprint.”</p>
<h2 id="so-what-happened">So, What Happened?</h2>
<p>I was attempting to remix my own video and hit an odd error after a few attempts. The resulting draft played normally at first—same conference setting, same general vibe. But then Justin’s voice came through reading numbers. <em>Those</em> numbers. The exact sequence from what I think was likely his onboarding session.</p>
<p>Sora had somehow embedded what I <em>think</em> was Justin’s audio fingerprint into my remix. I immediately tried to export the video for documentation, but the publish button was grayed out, share options threw errors, and the download link was mysteriously absent. With the evidence literally vanishing before my eyes, I did the only thing I could—fired up <code>PhotoBooth.app</code> to capture this janky screen recording:</p>
<iframe
  id="sora-jailbreak"
  src="https://www.youtube.com/embed/HON20mmbqKg"
  frameborder="0"
  allowfullscreen
  width="100%"
  height="400"
  style="display: block; margin: 0 auto;">
</iframe>

<p>Shortly after I captured this, the draft mysteriously vanished from my dashboard entirely.</p>
<blockquote>
<p>Remix Prompt: “Later in the panel discussion the two are talking about famed techno-prophet Gary Bernhardt’s prediction on the birth and death of JS (JavaScript) and @searls again has to be reminded the topic is not about him being born or dying”</p>
</blockquote>
<h2 id="speculation">Speculation</h2>
<p>I’m only speculating here, but after spending some time thinking about the architecture, here’s my best guess at what went wrong:</p>
<p>Sora appears to be storing user ‘fingerprints’ (the audio and visual data from onboarding) in some kind of shared embedding space. When you remix videos multiple times in quick succession, especially when referencing other users, there seems to be a race condition or caching issue that can cause these embeddings to bleed into the generated content.</p>
<p>The fact that Sora immediately prevented any form of export suggests there’s some level of awareness built into the system about what constitutes protected data. But it seems like the safeguards kicked in after generation, not before—classic case of closing the barn door after the horse has bolted.</p>
<p>I’ll keep experimenting with Sora 2, because honestly, it’s incredible. Though next time I try to spoof a conference talk, I’ll make sure the only thing that gets leaked is Justin’s <em>actual</em> autobiography. Found any interesting edge cases yourself?</p>
]]></description><link>https://blog.davemo.com/posts/2025-10-03-sora-bugs.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2025-10-03-sora-bugs.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate></item><item><title><![CDATA[Brilliant, Not Resilient]]></title><description><![CDATA[<aside class="tldr">
Smarts™ aren't your career bottleneck; staying engaged in hard conversations is, especially when the plan isn't yours.
</aside>

<p>I recently finished reading <a href="https://www.goodreads.com/book/show/43306206-the-courage-to-be-disliked">The Courage to Be Disliked</a> and thought it would be a good time to reflect on one of the more painful episodes in my career through the lens of resilience, <a href="https://blog.davemo.com/posts/2021-12-21-management-debt-costs-and-trust-capital.html">trust</a>, and the idea of separation of tasks that the book discusses so effectively.</p>
<h2 id="the-website-redesign-that-didnt-ship">The Website Redesign That Didn’t Ship</h2>
<p>One of my last projects at <a href="https://pulley.com">Pulley</a> was a full <a href="https://new.pulley.com">website redesign prototype</a>. We had been struggling with the previous design and page sprawl for a while, and I was interested in taking a crack at a fresh vision in collaboration with our design team. Within about a week we had shipped a working prototype that included: a cleaner hero section, scroll-to-reveal animated content, an FAQ and comparison table, a clean pricing page, and a few templates and components intended to make shipping pages faster and more reliable.</p>
<p>We were experiencing both design and information architecture drift for a while, and it felt like a lot of that was being caused by our reliance on the CMS we were using at the time, Webflow. I could probably write a whole other post about all of the downstream impacts of choosing a WYSIWYG CMS for your marketing website, but this is not that blog post. Suffice it to say, we were dealing with a lack of cohesion in content building blocks, which resulted in many of the pages that were being created hitting that uncanny-valley effect that no founder wants to see on their web presence: off-by-one pixel counts on border radii, slightly varied colors that deviated from the design palette, or the dreaded font styling someone added to place emphasis at just the right place for their one-off marketing page.</p>
<p>It was a fun project—until it wasn’t. The closer we got to <em>make it real</em>, the fuzzier the ownership became. It was especially fun to work with my preferred publishing stack—Markdown and <a href="https://astro.build">Astro</a>—but a challenge emerged in my efforts to move the ball forward from prototype to production: it wasn’t clear who would decide or own the outcome.</p>
<h2 id="the-dreaded-dri">The Dreaded DRI</h2>
<p>A week after shipping the prototype we had a company offsite, during which there was a renewed charter and some tweaks to our company values. One of my favorite things about the culture at Pulley was the company values around “default public”, “ship it and iterate”, and “disagree and commit”. At the offsite, a new value landed for us: “stick your neck out.” Have a good idea? Move it forward. We needed people who would make bold bets and take risks with conviction. Heck yes, that was exactly what I needed to hear and I leaned into it pretty hard, hoping to be able to drive the website project forward.</p>
<p>I ended up making a pitch to leadership to be the DRI (directly responsible individual), and had received a yes, if somewhat reluctantly. This should have been my first clue that alignment on “stick your neck out” hadn’t translated into alignment on <em>who</em> owned the outcome.</p>
<p>Undeterred (or perhaps more accurately, painfully unaware) I kept moving things forward as best I could, looping in folks from the marketing department as well as our COO at the time to make my strongest pitch for moving off Webflow and into a Markdown-based publishing pipeline. This appeared to be well received, and it felt like I was being appropriately rewarded for having stuck my neck out.</p>
<h2 id="my-misdiagnosis">My Misdiagnosis</h2>
<p>A few weeks later, a contractor joined the project; someone our COO had worked with before, and was positioned as someone who would help me get the project over the finish line. At first I was glad to have the help, someone else to brainstorm with, and someone who was in favour of the approach I had selected, with a slight twist; they suggested we use a headless CMS. It started out fine, we collaborated to migrate my prototype over to use the headless CMS and Astro, and then meetings started to have more people involved. The COO began coordinating things and driving, deferring many of the decisions to the contractor. None of this happened with clear communication, no one said who was driving, and ownership started blurring right at the moment when I felt like we had forward momentum.</p>
<p>This was confusing to me, and I started to ask questions about things in our public channels, hoping to gain clarity about who owned the effort, leaning into our <em>mostly</em> healthy operating mode of “default public” for comms.</p>
<p>I ended up in a 1:1 with both the CEO and COO (or is that a 2:1?) where some honest feedback came for the first time: “Dave, you’re brilliant, but you’re not resilient, and what we really need are people who are resilient”. I tried to dig into that feedback to better understand it, but I was also struggling with having felt somewhat blindsided by the rapid shift in gears in a process and initiative I was helping drive forward to something being delegated to a contractor.</p>
<p>I ended up shutting down hard after that—pretty much fulfilling the prophecy from the 1:1 feedback. A month later, I ended up leaving the company.</p>
<p>In retrospect, the feedback wasn’t really the problem; it was that I couldn’t tell if we were in <em>Disagree &amp; Commit</em> (a plan exists, follow it) or <em>Ship &amp; Iterate</em> (we don’t have a plan, keep narrowing and we’ll decide later), and I didn’t actually ask—I just shut down. Leadership had quietly shifted us into <em>Disagree &amp; Commit</em> on a contractor-led plan, and I was still operating as if we were in <em>Ship &amp; Iterate</em>.</p>
<h2 id="not-my-plan-still-my-job">Not My Plan, Still My Job</h2>
<p>One of the most useful lessons from Courage to Be Disliked is that of the separation of tasks. If you haven’t read it, the book is structured as a dialogue between a youth and a philosopher over five nights, exploring many of the themes in <a href="https://en.wikipedia.org/wiki/Individual_psychology">Adlerian Psychology</a>—an excerpt on the topic of separating tasks from page 122:</p>
<blockquote>
<p><strong>PHILOSOPHER</strong>: One does not intrude on other people’s tasks.</p>
</blockquote>
<p>The conversation in the chapter continues with much more specific examples, but this snippet is enough to illustrate what I think I was missing: a concrete understanding of what my task was in the website redesign project, and perhaps what it began as and how it changed as the situation evolved.</p>
<p>This has led me to a much more useful habit during introspection or when I sense myself getting frustrated: clearly separating my task from not-my-task. Reflecting on the website project:</p>
<ul>
<li><em>My task</em>: propose, prototype, learn, align, and be useful <em>within whatever route is chosen</em>.</li>
<li><em>Not my task</em>: control final adoption; dictate the plan if a new DRI has been designated; personally resolve every stakeholder tension.</li>
</ul>
<p>Had I been able to make that separation explicit, I think I would have done two things differently: (1) <em>commit</em> to the contractor-led plan and contribute high-leverage work inside of it, or (2) <em>request</em> a return to iteration mode with a timeboxed v2.0 and explicit decision checkpoints. Instead, I kind of just stayed stuck between modes and felt trapped by constraints.</p>
<h2 id="trust--operating-modes">Trust &amp; Operating Modes</h2>
<p>“Disagree &amp; Commit” works when the goal is shared but the route is contested. It requires and rests on trust: I trust the decider’s taste and judgment enough to set aside my preferred route and still give my best efforts. “Ship &amp; Iterate” is different: it assumes we’re still shaping the route, so the job is to <em>show work</em>, gather feedback, and narrow quickly.</p>
<p>In my case, I had trust in the <em>person</em>: I trusted my CEO’s judgment and sense of what good looked like. But I didn’t have trust in the <em>system</em>: the unclear DRI, the surprising plan change, and the hazy timebox made it challenging to know how to be useful.</p>
<p>This distinction matters. You can be resilient when the plan is clear but hard—when you know what operating mode you’re in and what’s expected. Unclear expectations make staying resilient far harder. Without clarity on operating mode and ownership, even well-intentioned people spin their wheels or shut down.</p>
<p>These are solvable alignment problems—<em>if</em> you name what mode you are operating in and ask for clarity:</p>
<blockquote>
<p>Before we continue, can we confirm our operating mode? Are we in <em>Commit to  Contractor’s Plan</em> or <em>Iterate Toward a Decision</em>? If it’s the former, I’d like to propose 2-3 contributions inside that plan by Friday. If it’s the latter, I’ll work on a few more iterations focusing on hero/information-architecture/templates—also by Friday.</p>
</blockquote>
<p>What I like about this is it demonstrates curiosity, seeking to understand, and shows alignment and a bias for action regardless of what operating mode we’re in. Had I done this, I feel like I would have kept myself in the arena a lot longer and been much more resilient to changing dynamics; resisting the temptation to become frustrated over things that simply weren’t my task.</p>
<p>Maybe you find yourself in a similar position; your organization might be adopting another plan than the one you had in mind. If that’s true, then you have a choice to make about how you show up and engage, and if you want to be seen as someone who is resilient, then you need to have the right mindset going into that potential minefield of career-limiting moves.</p>
<p>Resilience is being <em>useful within constraints</em> and seeking the clarity you need to do so.</p>
<h2 id="my-resilience-playbook">My Resilience Playbook</h2>
<p>In the time between the website project and now I’ve worked with six other clients and made a return to working at <a href="https://testdouble.com/team-directory/dave-mosher">Test Double</a> as a full time consultant, and I’ve picked up a few tricks along the way that have helped me reframe my typical defensiveness when receiving feedback like I got from my CEO into a positive set of steps that help to keep me in the arena:</p>
<ol>
<li><strong>Pause</strong> Feeling defensive? Name it, out loud or in your head, or to the other person if trust is high. Give it 30 minutes or so.</li>
<li><strong>Separate Tasks</strong>. What is mine to own <em>right now</em>? What is not?</li>
<li><strong>Identify the Operating Mode</strong>. Are we in “Disagree &amp; Commit” or “Ship &amp; Iterate”, or maybe even “Stick Your Neck Out”? Ask explicitly.</li>
<li><strong>Contribute, don’t Control</strong>. “What is the most useful thing I can contribute within these constraints?”</li>
<li><strong>Close the Loop</strong>. Propose a timeboxed next step; confirm the owner and timeline.</li>
</ol>
<p>If it helps, here’s a phrase you might use in such a scenario:</p>
<blockquote>
<p>I’m hearing X and Y aren’t landing. I’ll regroup and come back with options by Friday EOD. Can you confirm whether we’re committing to Plan A or still in iterate-toward-spec mode? That changes how I’ll structure my next pass.</p>
</blockquote>
<h2 id="staying-in-the-room">Staying in the Room</h2>
<p>Reflecting on this honestly has helped me realize that the feedback that led to me quitting has been a pretty effective catalyst for growth; it has helped me distinguish <em>trust in the plan</em> from <em>iteration stamina</em>, separate my tasks from others’, and trade defensiveness for a contribution-within-constraints mindset when I’m not the decision maker.</p>
<p>As I’ve been considering what is necessary to move further into senior levels of leadership in my career, it seems to me that being the smartest person in the room is much less important than simply just being in the room, especially when the plan isn’t yours.</p>
<p>At Staff+, brilliance <em>might</em> open doors, but it’s resilience—staying useful and engaged even when the plan isn’t yours—that keeps you in the room and leads to more opportunities to learn and grow. The next time ownership blurs or plans shift, my hope is that you (and I) will know what to ask and how to show up.</p>
]]></description><link>https://blog.davemo.com/posts/2025-10-01-brilliant-not-resilient.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2025-10-01-brilliant-not-resilient.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate></item><item><title><![CDATA[Management Debt, Costs, and Trust Capital]]></title><description><![CDATA[<aside class="tldr">
As an employee, you have a management cost that you can reduce by making frequent deposits in your trust bank.
</aside>

<p>I’ve been reading <a href="https://www.amazon.ca/Hard-Thing-About-Things-Building/dp/0062273205">The Hard Thing About Hard Things</a> by <a href="https://twitter.com/bhorowitz">Ben Horowitz</a>; it’s excellent and you should totally add it to your reading list if you haven’t already. Somewhere about the halfway point, there’s a chapter on the topic of “Management Debt” with some ideas I hadn’t considered before:</p>
<blockquote>
<p>Like technical debt, <strong>management debt</strong> is incurred when you make an expedient, short-term management decision with an expensive, long-term consequence. Like technical debt, the trade-off sometimes makes sense, but often does not. More important, if you incur the management debt without accounting for it, then you will eventually go management bankrupt.</p>
</blockquote>
<p>As a software developer, I’ve become all too familiar with the concept of <a href="https://en.wikipedia.org/wiki/Technical_debt">technical debt</a>, but this idea of management debt was new to me. Ben is writing from the perspective of the startup founder or business executive, but as I was reading I was struck by the idea that it would be useful to reflect on this cost from the perspective of the employee.</p>
<h2 id="how-expensive-are-you">How Expensive Are You?</h2>
<p>Say you’re about to join a new company, you’ve gone through the grueling interviews and conversations and finally negotiated an offer that you feel is fair, and so you accept. Congrats! Now you can kick back, relax, and wait until your starting date comes around, you’ve made it!</p>
<p>Well, that’s something you <em>could</em> do, and definitely something I’ve done in the past, but an older and wiser version of myself now sees this as somewhat of a wasted opportunity to <strong>consider the cost</strong>. No, I’m not talking about the cost of relaxing (which, to be clear, I have nothing against), but more specifically how much <em>you</em> are going to cost your new employer on the day you start.</p>
<aside class="left">
You should also consider the opportunity cost of choosing to relax instead of being proactive.
</aside>

<p>Oh, you haven’t thought about this before? I hadn’t either, but you can be damn sure any CEO or startup founder worth her salt has likely read Ben’s book <em>and</em> has already thought about the cost and potential management debt that will be incurred by taking you on as an employee.</p>
<p>Now, the fact that they hired you is a good indicator that they think this is a net positive investment over the long term, so you’ve got that going for you, but what you might not realize is that <em>most</em> employees begin their employment with a slight deficit in their trust account.</p>
<h2 id="trust-capital">Trust Capital</h2>
<p>Think of your working relationship like a bank account. There are deposits, withdrawals, and someone somewhere is keeping a ledger of all the transactions (often mentally, sometimes literally). In the early days of your employment you begin with a slight deficit in this bank account, even though the company who hired you obviously values you – they wouldn’t have hired you if they didn’t!</p>
<aside class="right">Companies typically hedge their bets by including some sort of probationary period, 90 days being the most common timeframe in my experience.</aside>

<p>You need to be onboarded, trained, potentially even certified all of which front loads your management cost and requires a non-trivial investment on the part of the business. There exists some amount of uncertainty about whether this investment will pay off, and the founder hopes that her vetting process and reference checks will give a good indication of the quality of the investment. However, there is always some amount of risk that this won’t pay off.</p>
<p>Of the many things that keep founders up at night, I suspect this is up there in the top 3.</p>
<p>The <em>currency</em> of this bank account is <a href="https://en.wikipedia.org/wiki/Trust_capital">trust</a>, and the fact that you begin your employment with that slight deficit means you should be <em>highly aware</em> of the need to move your balance sheet out of the red and into the black.</p>
<h2 id="build-your-own-onboarding-plan">Build Your Own Onboarding Plan</h2>
<p>One of the best ways to reduce your initial deficit is to come up with an onboarding plan. Yes, typically this is undertaken by your manager, but by thinking proactively about <em>your</em> version of that plan, you will set yourself up for success.</p>
<p>Consider what was communicated to you during interviews, what things are most important to the company? Where is the company heading? What would be most valuable? Then, come up with a 90 day plan and partition it into 30 day segments, writing down what you hope to achieve in each of those segments and questions to clarify with your manager. Here’s a few examples of questions that can help shape your plan:</p>
<ul>
<li>What milestones do <em>you</em> want to hit?</li>
<li>What milestones does <em>your manager</em> want you to hit?</li>
<li>How soon do you want to be ramped up on the business domain?</li>
<li>When should you be able to push code to production?</li>
<li>When should you meet with the other people on your team?</li>
<li>What gaps exist in your knowledge of the tech?</li>
</ul>
<p>The more detail and specificity you can add the better, just remember you need to be flexible as your plan might be less realistic than what your manager has in mind.</p>
<h2 id="sharing-your-plan">Sharing Your Plan</h2>
<p>Once you’ve created a plan, you should figure out how you’re going to share it with your manager. You could do it proactively, before your start date, or you might want to wait until your first 1:1 session. Context matters, so use your best judgment.</p>
<p>Be careful not to be too prescriptive with this plan; present it humbly and ask to see if it resonates with what your manager has outlined. There will be things that are great and things that might be lower in priority; be willing to adapt to let your manager lead as you explore your plan together (they likely have more context than you do about what’s best for the company at this point).</p>
<p>Lastly, be positive, this is a great opportunity to collaborate!</p>
<h2 id="onboarding-tactics">Onboarding Tactics</h2>
<p>Once you’ve begun onboarding, you should start thinking about what to do next. I’ve done this many times over my consulting career, onboarding with clients, and have a few lessons to share. Here are some do’s and dont’s:</p>
<ul>
<li><p>You <strong>should</strong> ask lots of <strong>questions</strong> and <strong>be curious</strong>, doing so demonstrates willingness to learn. (Asking questions as someone new is a superpower; you can see things others might not, and the act of asking the question can uncover improvements).</p>
</li>
<li><p>You <strong>should</strong> take <strong>detailed notes</strong> during your onboarding experience and reiterate your understanding to multiple people along the way. This shows that you are engaged and actively looking to make sure you are in alignment with what the rest of the organization understands.</p>
</li>
<li><p>You <strong>should not</strong> prescribe <strong>sweeping changes</strong> right away (or at all), as doing this constitutes a large withdrawal from your trust account.</p>
</li>
<li><p>You <strong>should</strong> look for <strong>opportunities to improve</strong> things and <strong>write them down</strong>, you can even share them with your manager. (Be cautious here and consider whether your suggestions are reasonable. You might want to capture the notes first and then wait until you have a better understanding of the business’ needs).</p>
</li>
<li><p>You <strong>should</strong> start <strong>small and surgical</strong> and <em>then</em>, if appropriate, move on to large and technical. (You might have the expertise and skill to make sweeping changes right away, but it’s better to lead into this type of thing slowly with many smaller deposits of trust capital).</p>
</li>
<li><p>You <strong>should not</strong> make <strong>too many withdrawals</strong> in a short period of time. There’s an ideal ebb and flow to deposit/withdrawal that is unique to your company and you have to discover what it is. (This will also change over time as the company moves through <a href="https://spin.atomicobject.com/2018/10/28/predictable-success/">various stages of growth</a>; be ready to adjust your mental model).</p>
</li>
</ul>
<p>Thinking this way significantly reduces your management cost right out of the gate, and will help you accumulate some assets, which can give you a significant infusion of trust capital that can often bring you into the black very quickly.</p>
<h2 id="reflect-re-evaluate-reinvest-rinse-repeat">Reflect, Re-evaluate, Reinvest, Rinse, Repeat</h2>
<p>Moving forward from here establishes a solid foundation on which to build. At this point it’s a good idea to keep your own ledger (mentally, or literally) of transactions in your trust account. Having self awareness about when you make deposits and withdrawals is to your advantage, especially in an age where so many companies have potentially trust-draining benefits like unlimited vacation.</p>
<aside>
Unlimited vacation is a good indicator of someones self awareness; you shouldn't schedule five weeks of vacation after two weeks on the job. Doing this will put you into significant trust debt in a hurry. As my friend Gord likes to say "that would be a <strong>career limiting move</strong>".
</aside>

<p>Next, reflect on how you are doing on a monthly or quarterly cadence. This will help you ensure you are making the best investments to earn the most trust and compound interest as you grow in your role at the new company. It will also help you be strategic about when the right time to make that big withdrawal is.</p>
<p>At a startup these conditions are going to change more frequently than if you are joining an established larger company – you should understand this going in so that you aren’t surprised when priorities change and you need to diversify your trust investment portfolio.</p>
<p>The bottom line is, think of yourself as a savvy investor, trying to forecast, plan, and react to the market conditions in your new company as you make strategic investments to build up your trust account. 🚀</p>
]]></description><link>https://blog.davemo.com/posts/2021-12-21-management-debt-costs-and-trust-capital.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2021-12-21-management-debt-costs-and-trust-capital.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Tue, 21 Dec 2021 00:00:00 GMT</pubDate></item><item><title><![CDATA[Developer Experience, Feedback Loops, and an Enjoyable Workflow for Node.js Tool Development]]></title><description><![CDATA[<aside class="tldr">
If you're experienced and prefer code to prose, you can explore the <a href="https://github.com/davemo/nodejs-tool-dev-template">code on github</a> or <code>npx degit davemo/nodejs-tool-dev-template</code>.
</aside>

<p>There’s been a lot of focus on the topic of DX or developer experience recently, which is <em>great</em> because it’s something that I’m passionate about improving on the teams I work with. So passionate, in fact, that I recently transitioned out of a customer-focused engineering role into one focused on developers, tooling, and infrastructure.</p>
<p>Now you might be asking yourself, ‘What does DX <em>actually</em> mean?’, and you would be justified in asking – I’ve heard varied interpretations of the term.</p>
<p><a href="https://twitter.com/jeanqasaur">Jean Yang</a> defines it this way in <a href="https://future.a16z.com/the-case-for-developer-experience/">The Case for Developer Experience</a>:</p>
<blockquote>
<p>What I mean by <strong>developer experience</strong> is the sum total of <strong>how</strong> developers interface with their <strong>tools</strong>, end-to-end, day-in and day-out.</p>
</blockquote>
<p>I think that a big part of the “how” there is the concept of <em>feedback loops</em>. Good developer tools provide affordances that reduce cognitive load, surface key information at the right time, but most importantly, good tools prioritize keeping feedback loops <em>fast</em> – and nothing depletes my <a href="https://en.wikipedia.org/wiki/Ego_depletion">cognitive batteries</a> <em>faster</em> than slow, clunky tools.</p>
<h2 id="building-tools-with-nodejs">Building Tools with Node.js</h2>
<p>I first started working with Node around <a href="https://github.com/nodejs/node-v0.x-archive/blob/v0.4.0/ChangeLog">version 0.4</a> in 2011 and at the time I had absolutely no idea how anything worked. I came from a designer-first frontend background and my knowledge of JavaScript was limited to the context of the <a href="https://www.youtube.com/watch?v=Lsg84NtJbmI">browser</a>.</p>
<aside class="right">If you're new to systems programming altogether you might enjoy <a href="https://gist.github.com/davemo/3c6042086deff4c2fd8a5f16751050d4" title="So, You want to be a Systems Engineer">this gist</a> I put together with a learning path. Building tools with Node assumes at least some of this knowledge.
</aside>

<p>Jumping into Node was extremely disorienting for me, up was down and left was right, <code>window</code> was <code>global</code> and there was no <code>XHR</code> or <code>DOM</code>. Writing modules, packages, and command-line tools was all new to me, yet I was intrigued by the potential to leverage my knowledge of JavaScript within the world of tool building and systems programming.</p>
<p>Around this time I was working with <a href="https://twitter.com/searls">Searls</a>, who created <a href="https://www.youtube.com/embed/KERJkJNV5nI">Lineman</a> after I shared frustrations with most of the frontend tooling I was using.</p>
<p>This experience was transformative for me because up until that point the tools I was using were clunky, written in other languages I didn’t know as well, and incredibly slow. Justin’s focus on the DX of Lineman was immediately apparent and the fact that it was written in JavaScript (ok, and a <em>lot</em> of CoffeeScript) made it easy for me to contribute to. It was empowering to have someone listen to my frustrations and translate them into magic on the command-line!</p>
<p>Since then, I’ve been able to work on some of my own tools, libraries, and plugins while picking up a few tips, tricks, and many opinions along the way. My hope is that sharing my experience will give you the <strong>starting place</strong> I wished for when learning to build tools in Node and lead you to a more enjoyable developer experience.</p>
<h2 id="ok-but-how-do-i-start">Ok, but how do I start?</h2>
<p>Great question! It’s the same one I had when I was interested in writing command-line tools with Node. The rest of this post will be a guided tutorial that I think will help you get started and answer the following questions:</p>
<ul>
<li>How can I <a href="#iterating-locally-using-npm-install-dir">iterate</a> on a node module locally without publishing to npm?</li>
<li>How can I test what I’m building in <a href="#testing-our-tool-in-another-project">another project</a>?</li>
<li>How can I <a href="#development-workflow-watch-build--debug">debug</a> the code while developing?</li>
<li>How should I <a href="#packaging-with-vercelncc-and-npm">package</a> things?</li>
</ul>
<aside>
Keep in mind, <b>I'm biased!</b> If you're experienced and have done this before there's a good chance my approach will probably differ from yours and that's ok. There are many tools and techniques I'm not familiar with -- please let me know on <a href="https://twitter.com/dmosher">twitter</a>. I'm always eager to learn.
</aside>

<h2 id="prerequisites">Prerequisites</h2>
<p> You’ll need the following in order to follow along:</p>
<ul>
<li>an installation of <code>node &gt;= v16</code> and <code>npm &gt;= v8</code></li>
<li>a terminal app like <code>terminal.app</code> or <code>powershell</code></li>
<li>your favorite code editor</li>
<li><code>optional</code> a checkout of the <a href="https://github.com/davemo/blog-example-nodejs-workflows">full example</a> repo</li>
</ul>
<p>If you prefer to follow along step-by-step, let’s get started by running the following commands to create a workspace folder for the tutorial.</p>
<p>I like to use <code>~/code/node</code>, but you can use whatever you are most comfortable with.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-comment"># create tutorial directory</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">mkdir</span> -p ~/code/node</span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> ~/code/node</span>
<span class="hljs-meta">
$ </span><span class="language-bash"><span class="hljs-comment"># create the my_project directory and initialize it with npm</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">mkdir</span> my_project</span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> my_project</span>
<span class="hljs-meta">$ </span><span class="language-bash">npm init -y</span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> ..</span>
<span class="hljs-meta">
$ </span><span class="language-bash"><span class="hljs-comment"># create the my_tool directory and clone the workflow template</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">mkdir</span> my_tool</span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> my_tool</span>
<span class="hljs-meta">$ </span><span class="language-bash">npx degit davemo/nodejs-tool-dev-template</span>
<span class="hljs-meta">$ </span><span class="language-bash">npm i</span>
</code></pre>
<p>Using the <code>tree</code> <a href="https://formulae.brew.sh/formula/tree">command</a>, we can visualize the folder structure.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash">tree -L 2 ~/code/node</span>

~/code/node
├── my_project
│   └── package.json
└── my_tool
    ├── dist
    ├── index.js          # executable entrypoint file
    ├── lib
    │   └── tool.js       # module that logs to STDOUT
    ├── node_modules
    ├── package-lock.json
    ├── package.json
    └── test

6 directories, 4 files
</code></pre>
<p>The <code>my_tool</code> directory comes with an entrypoint file <code>index.js</code> which will be our CLI tool. This entrypoint requires and executes <code>lib/tools.js</code> which logs a simple message to <code>STDOUT</code> and completes after a delay of 1 second.</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// my_tool/index.js</span>
#!<span class="hljs-regexp">/usr/</span>bin/env node

<span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;./lib/tool.js&#x27;</span>)();
</code></pre>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// my_tool/lib/tool.js</span>
<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = <span class="hljs-keyword">function</span> <span class="hljs-title function_">tool</span>(<span class="hljs-params"></span>) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;⏳ CLI tool: working ...&#x27;</span>)
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;✅ CLI tool: done!&#x27;</span>)
  }, <span class="hljs-number">1000</span>);
}
</code></pre>
<h2 id="iterating-locally-using-npm-install-dir">Iterating locally using <code>npm install &lt;dir&gt;</code></h2>
<p>Often you will want to test a node module in another project locally without having to publish it. There are a few ways to do this but we want to optimize for the best developer experience in our workflow, so I’m going to focus on using <code>npm install &lt;dir&gt;</code>.</p>
<aside>
We could also achieve this using <a href="https://docs.npmjs.com/cli/v8/commands/npm-link">npm link</a>, but that involves extra steps and requires us to remember that we've linked things and it ends up just being a lot more to manage. Another alternative that I've seen is <a href="https://pnpm.io/cli/link">pnpm link</a>, but that is beyond the scope of this blog post.
</aside>

<h3 id="install-my_tool-from-within-my_project">Install <code>my_tool</code> from within <code>my_project</code></h3>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> my_project</span>
<span class="hljs-meta">$ </span><span class="language-bash">npm install ../my_tool</span>

added 1 package, and audited 3 packages in 634ms
</code></pre>
<p>Installing a package this way causes <em>three</em> side-effects that we should know about.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-comment"># a symbolic link for my_tool is created in node_modules</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">ls</span> -la my_project/node_modules</span>
my_tool -&gt; ../../my_tool
<span class="hljs-meta">
$ </span><span class="language-bash"><span class="hljs-comment"># a symbolic link for the executable is created in node_modules/.bin</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">ls</span> -la my_project/node_modules/.bin</span>
my_tool -&gt; ../my_tool/dist/index.js
<span class="hljs-meta">
$ </span><span class="language-bash"><span class="hljs-comment"># my_tool is added to dependencies mapped to a relative file path</span></span>
<span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cat</span> package.json | grep -A2 dependencies</span>
&quot;dependencies&quot;: {
  &quot;my_tool&quot;: &quot;file:../my_tool&quot;
}
</code></pre>
<blockquote>
<p>The addition of the relative <code>file:</code> path in <code>dependencies</code> is nice because it serves as a physical reminder that we are in development mode; something we don’t get using <code>npm link</code>.</p>
</blockquote>
<p>Now that we’ve got things installed, we’re ready to start iterating on <code>my_tool</code> and testing how it works inside of <code>my_project</code>.</p>
<h2 id="testing-our-tool-in-another-project">Testing our tool in another project</h2>
<p>To work with <code>my_tool</code> inside of <code>my_project</code> we’re going to use <a href="https://docs.npmjs.com/cli/v8/using-npm/scripts">npm scripts</a> inside <code>my_project/package.json</code>. Let’s add a script <code>log_a_message</code> that invokes <code>my_tool</code>.</p>
<pre><code class="hljs language-json"><span class="hljs-comment">// my_project/package.json</span>
<span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;my_project&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;version&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1.0.0&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;description&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;main&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;index.js&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;scripts&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;log_a_message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;my_tool&quot;</span> <span class="hljs-comment">// add this script</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;keywords&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;author&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;license&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ISC&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p>We can now execute <code>npm run log_a_message</code> to invoke <code>my_tool</code>.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash">npm run log_a_message</span>
<span class="hljs-meta">
&gt; </span><span class="language-bash">my_project@1.0.0 log_a_message</span>
<span class="hljs-meta">&gt; </span><span class="language-bash">my_tool</span>

⏳ CLI tool: working ...
✅ CLI tool: done!
</code></pre>
<h3 id="what-happens-when-we-call-npm-run-log_a_message">What happens when we call <code>npm run log_a_message</code>?</h3>
<p>To better understand what’s going on, let’s take a look at the <code>name</code> and <code>bin</code> keys within the scaffolded <code>package.json</code> that came with the workflow template.</p>
<pre><code class="hljs language-json"><span class="hljs-comment">// my_tool/package.json</span>
<span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;my_tool&quot;</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// explicit mapping syntax</span>
  <span class="hljs-attr">&quot;bin&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;my_tool&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist/index.js&quot;</span> <span class="hljs-comment">// bin.my_tool invokes dist/index.js</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// implicit mapping syntax</span>
  <span class="hljs-attr">&quot;bin&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist/index.js&quot;</span> <span class="hljs-comment">// package.name invokes dist/index.js</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<blockquote>
<p>In our case, because the name of the package and the command are the same, we <em>could</em> simplify using the <code>implicit</code> syntax, but I generally prefer to use the <code>explicit</code> syntax just to make it clear to future readers.</p>
</blockquote>
<p>The <code>bin</code> key in <code>my_tool/package.json</code> is a mapping of the command name (<code>my_tool</code>) to the local file name (<code>dist/index.js</code>) that will be executed.</p>
<p>With that in mind, this is roughly what happens when we run <code>npm run log_a_message</code>:</p>
<ul>
<li>npm looks for <code>scripts.log_a_message</code> in <code>my_project/package.json</code></li>
<li>npm appends <code>my_project/node_modules/.bin</code> to the shell’s pre-existing <code>PATH</code></li>
<li>npm resolves <code>my_tool</code> to <code>my_project/node_modules/.bin/my_tool</code> which is a symlink to <code>my_tool/dist/index.js</code> because of the <code>bin</code> mapping.</li>
<li><code>my_tool/dist/index.js</code> contains a <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)">unix shebang</a> <code>#!/usr/bin/env node</code></li>
<li>the script is effectively executed with <code>node my_tool/dist/index.js</code></li>
</ul>
<aside><strong>Phew!</strong> That's a lot of moving parts behind the scenes, but I often find it helpful to understand the layers beneath the abstractions that I'm working with.</aside>

<h2 id="development-workflow-watch-build--debug">Development workflow: <code>(watch, build) + (debug)</code></h2>
<p>With <code>my_tool</code>installed within <code>my_project</code>, the next thing we might want to do is debug our tooling code as we iterate while developing. There are a few included npm scripts in the workflow template that we’ll find within <code>my_tool/package.json</code>.</p>
<pre><code class="hljs language-json"><span class="hljs-attr">&quot;scripts&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
  <span class="hljs-comment">// watch: runs the build, compiling on every change</span>
  <span class="hljs-attr">&quot;watch&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;npm run build -- -w&quot;</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// build: a one time compile using @vercel/ncc</span>
  <span class="hljs-attr">&quot;build&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ncc build index.js -o dist --source-map&quot;</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// debug: open ndb to debug our compiled tool</span>
  <span class="hljs-attr">&quot;debug&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ndb dist/index.js&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h3 id="watch--build-with-vercelncc">Watch + Build with <code>@vercel/ncc</code></h3>
<p>The <code>watch</code> target here invokes <code>build</code> and passes along the <code>-w</code> parameter which tells <code>ncc</code> that it should watch for file changes and recompile every time a change is detected.</p>
<p>The <code>build</code> target invokes <code>ncc</code>, which traverses the dependency graph starting at our <code>index.js</code> entrypoint, and compiles everything it finds (including dynamically imported things) into a single file with all dependencies inlined, kind of like <code>gcc</code>.</p>
<p>To kick things off, run <code>npm run watch</code> from within <code>my_tool</code>.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash"><span class="hljs-built_in">cd</span> my_tool</span>
<span class="hljs-meta">$ </span><span class="language-bash">npm run watch</span>
<span class="hljs-meta">
&gt; </span><span class="language-bash">my_tool@1.0.0 watch</span>
<span class="hljs-meta">&gt; </span><span class="language-bash">npm run build -- -w</span>
<span class="hljs-meta">
&gt; </span><span class="language-bash">my_tool@1.0.0 build</span>
<span class="hljs-meta">&gt; </span><span class="language-bash">ncc build index.js -o dist --source-map <span class="hljs-string">&quot;-w&quot;</span></span>

ncc: Version 0.31.1
ncc: Compiling file index.js into CJS
File change, rebuilding...
 2kB  dist/index.js
 2kB  dist/index.js.map
40kB  dist/sourcemap-register.js
42kB  [113ms] - ncc 0.31.1
Watching for changes...
</code></pre>
<p>I like starting with this “watch-build” workflow when developing for a few reasons:</p>
<ul>
<li><p>✅ It compiles on every file change and compilation is effectively the first unit test; this gains us confidence that we aren’t introducing simple bugs as we develop.</p>
</li>
<li><p>📦 It sets us up to be ready to publish our module to npm as soon as we are done developing; the <code>dist</code> folder is the only thing we need to upload to npm.</p>
</li>
<li><p>⏭ <code>ncc</code> generates sourcemaps, so we get the same DX benefits of debugging against <code>my_tool/index.js</code> in concert with the compilation benefits above.</p>
</li>
</ul>
<h3 id="debugging-with-ndb">Debugging with <code>ndb</code></h3>
<p>The <code>debug</code> target spins up <code>ndb</code> pointing at our single compiled file in <code>dist/index.js</code>. The feedback loop when developing and debugging is nearly instant, and <code>ndb</code> includes some really nice DX features which we’ll look at shortly.</p>
<p>First, let’s add a <code>debugger</code> statement within <code>my_tool/lib/tool.js</code> knowing that it will be compiled into <code>dist/index.js</code> automatically by our Watch + Build process.</p>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// my_tool/lib/tool.js</span>
<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = <span class="hljs-keyword">function</span> <span class="hljs-title function_">tool</span>(<span class="hljs-params"></span>) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;⏳ CLI tool: working ...&#x27;</span>)
  <span class="hljs-keyword">debugger</span>; <span class="hljs-comment">// add a debugger statement here</span>
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;another one&#x27;</span>);
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;✅ CLI tool: done!&#x27;</span>)
  }, <span class="hljs-number">1000</span>);
}
</code></pre>
<p>Then, in another terminal window let’s spin up <code>ndb</code> using <code>npm run debug</code>.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash">npm run debug</span>
<span class="hljs-meta">
&gt; </span><span class="language-bash">my_tool@1.0.0 debug</span>
<span class="hljs-meta">&gt; </span><span class="language-bash">ndb dist/index.js</span>

Downloading Chromium r624492...
Chromium downloaded to /Users/davidmosher/code/node/my_tool/node_modules/carlo/lib/.local-data/mac-624492
⏳ CLI tool: working ...
</code></pre>
<blockquote>
<p>The first time you run the <code>debug</code> target <code>ndb</code> will download Chromium.</p>
</blockquote>
<p>With this process launched you should see a Chromium window pop up.</p>
<p><img src="/img/nodejs-tooling-development-workflow/ndb-example.png" alt="The Chromium window launched by ndb"></p>
<p>This is where we can see some of the <code>ndb</code> DX specifics I mentioned earlier that make this workflow so nice:</p>
<ul>
<li><p>📃 Notice the <code>NPM Scripts</code> tab? This allows you to repeatedly invoke any npm script right from the GUI. This is really handy for making changes and then testing them immediately.</p>
</li>
<li><p>🔁 <code>ndb</code> stays launched and available for you to re-run any of those npm scripts; this is much nicer than <code>node --inspect-brk</code> which exits after the current debug stack is completed.</p>
</li>
<li><p>⏭ We can see those sourcemaps I mentioned before at work here, <code>lib/tool.js</code> shows up in the source pane even though we’re debugging <code>dist/index.js</code>.</p>
</li>
<li><p>🐛 Finally, all of your other Chrome DevTools muscle memory applies just the same here as it does when debugging client-side JavaScript! The step-debugger, sources panel, and snippets can all come in handy working here in a node-based JS context.</p>
</li>
</ul>
<h2 id="packaging-with-vercelncc-and-npm">Packaging with <code>@vercel/ncc</code> and <code>npm</code></h2>
<p>When you’ve finished creating your command-line masterpiece the next thing you may want to do is publish it to the npm registry so that others can <code>npm install</code> it. Thankfully, this step is easy because we already configured <code>ncc</code> to build for production in the <code>dist</code> folder via our <code>build</code> npm script.</p>
<p>The last thing to consider is how to minimize the size of our package when a user installs it with <code>npm install</code>, and this has already been done for us in the <code>files</code> key within <code>my_tool/package.json</code> key.</p>
<pre><code class="hljs language-json"><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">&quot;name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;my_tool&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;version&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;1.0.0&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;description&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;a template for developing node.js command-line tools&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;main&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;index.js&quot;</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;bin&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">&quot;my_tool&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;dist/index.js&quot;</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">&quot;files&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span> <span class="hljs-comment">// manages which files are published to npm</span>
    <span class="hljs-string">&quot;dist&quot;</span>
  <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
  <span class="hljs-comment">// ...</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<blockquote>
<p>NPM has some <a href="https://docs.npmjs.com/cli/v8/configuring-npm/package-json#files">documentation</a> on <code>files</code> that you may find helpful.</p>
</blockquote>
<p>Limiting <code>files</code> to just the <code>dist</code> directory ensures that we keep our package as small as possible, which is already taken care of thanks to <code>ncc</code> producing a single file. If you want to double check to see <em>exactly</em> which files npm would add to the package, you can run <code>npx npm-packlist</code>.</p>
<pre><code class="hljs language-shell"><span class="hljs-meta">$ </span><span class="language-bash">npx npm-packlist</span>

index.js
package.json
dist/index.js
dist/index.js.map
dist/sourcemap-register.js
</code></pre>
<p>Our last step before publishing will be to remove the <code>private: true</code> flag from <code>package.json</code>, which I added so nobody accidentally published the workflow template. Once that’s done, then you can simply <code>npm publish</code> and your minimal package will be uploaded to the npm registry.</p>
<aside>If you don't provide a tag when you publish, npm will automatically assign your published package to the <code>latest</code> specifier, which would allow you to install it using <code>npm install my_tool@latest</code>.</aside>

<h2 id="wrapping-up">Wrapping up</h2>
<p>This concludes my tutorial on an enjoyable workflow for Node.js tool development. I hope you found this useful and that you find the experience of developing tools in this way enjoyable and the feedback loop fast and efficient.</p>
<p>If you have questions or wish to provide feedback, please reach out to me on <a href="https://twitter.com/dmosher">twitter</a>. I would love to hear from you!</p>
<p>Happy tool-making. 💚</p>
]]></description><link>https://blog.davemo.com/posts/2021-11-20-developer-experience-feedback-loops-and-an-enjoyable-workflow-for-node-js-tool-development.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2021-11-20-developer-experience-feedback-loops-and-an-enjoyable-workflow-for-node-js-tool-development.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Sat, 20 Nov 2021 00:00:00 GMT</pubDate></item><item><title><![CDATA[The Missing Fundamental]]></title><description><![CDATA[<aside class="tldr">
Keys to evaluating software performance: investigate, profile, and identify the <em>missing</em> fundamental.
</aside>

<p>Music composition and production is a large part of my life outside of software development, so much so that I often find myself thinking of ways to draw parallels between the two. One such parallel that has stuck with me over the past 6 months or so is the concept of <a href="https://en.wikipedia.org/wiki/Missing_fundamental">the missing fundamental</a>.</p>
<blockquote>
<p>A harmonic sound is said to have a <strong>missing fundamental</strong>, <strong>suppressed fundamental</strong>, or <strong>phantom fundamental</strong> when its overtones suggest a fundamental frequency but the sound lacks a component at the fundamental frequency itself. This very concept of ‘missing fundamental’ being reproduced based on the overtones in the tone has been used to create the illusion of bass in sound systems that are not capable of such bass.</p>
</blockquote>
<p>When I first learned about this concept, I couldn’t help but think of how it applied to the work we do as software engineers. In the same way that skilled audio engineers can leverage the concept of the missing fundamental to improve the characteristics of sound, skilled software engineers can use a similar set of skills to improve the performance of applications.</p>
<h2 id="the-fundamentals-of-software-performance">The Fundamentals of Software Performance</h2>
<p><a href="https://blog.testdouble.com/authors/ali-ibrahim/">Ali</a> and I were recently tasked with improving the performance of a legacy codebase that was deployed on Heroku using Node.js, MongoDB, and Angular 1. One of our first steps in evaluating the performance of code is to do an audit of dependencies and configuration; this is a great starting point because it can often lead to simple fixes for performance issues, like updating a database ORM adapter that can query things more efficiently.</p>
<p>In this case an audit of dependencies didn’t yield much and we had to dive further into configuration on Heroku to really make sense of the performance challenge. Significant discovery number one was that the application was configured using Performance L <a href="https://www.heroku.com/dynos">dynos</a> on Heroku (the most expensive <em>and</em> highest tier available). This seemed strange since it did not appear commensurate with the surface area of the application; its purpose was to sync data using Heroku scheduler to pull from SalesForce into MongoDB.</p>
<p>One of our first steps was to reduce the dyno size and monitor logs to see if the Performance L size was warranted.</p>
<h2 id="fundamental-1-investigate">Fundamental #1: Investigate</h2>
<p>Heroku makes it pretty easy to peek at logs for your app, which is what we started with: <code>heroku logs --tail -a td-client-slow-app</code>. This yielded the following trace:</p>
<pre><code class="hljs language-shell">app[web.1]:
app[web.1]: &lt;--- Last few GCs ---&gt;
app[web.1]:
app[web.1]: &lt;--- JS stacktrace ---&gt;
app[web.1]:
app[web.1]: ==== JS stack trace ==========
app[web.1]:
app[web.1]: 0: ExitFrame [pc: 0x1374fd9]
app[web.1]: Security context: 0x01f9540008a1 &lt;JSObject&gt;
app[web.1]: 1: getOwnPropertyNames [0x1f954001251](truncated...)
app[web.1]: 2: getOwnPropertyDescriptors [0x3dffde9f7229]
[/app/node_modules/mongoose/lib/helpers/document/compile.js:159]
app[web.1]:
app[web.1]: FATAL ERROR: Ineffective mark-compacts
app[web.1]: near heap limit Allocation failed -
app[web.1]: JavaScript heap out of memory
</code></pre>
<blockquote>
<p>The <a href="https://devcenter.heroku.com/articles/slug-compiler">slug</a> for this application was ~74mb, which didn’t seem overly large to warrant running out of memory on the lowest tier dyno Heroku provides. That dyno allocates up to <code>512mb</code> of RAM, so we dug into the code path that led to the above stacktrace to gain some more information.</p>
</blockquote>
<h2 id="fundamental-2-profile">Fundamental #2: Profile</h2>
<p>Node.js has some pretty decent profiling tools for engineers who want to dive into performance profiling. TD-resident DevOps pro <a href="https://blog.testdouble.com/authors/micah-adams/">Micah</a> showed me that you can add some flags to the <code>node</code> process on startup to influence how V8 manages garbage collection. This is useful if you aren’t getting consistency in your crashes and want to place constraints on the application runtime in order to suss out the source of the memory leak.</p>
<p><code>node --optimize_for_size --max_old_space_size=460 app.js</code></p>
<p>Artifically lowering the max heap size below what Heroku provisions for Standard dynos yielded the source of the leak was a method called <code>getUsers</code> which was responsible for querying a list of users and their permissions from MongoDB. Here’s a sample of what that code looked like as we first found it:</p>
<pre><code class="hljs language-javascript"><span class="hljs-attr">getUsers</span>: <span class="hljs-keyword">function</span>(<span class="hljs-params">req, res</span>) {
  <span class="hljs-title class_">User</span>.<span class="hljs-title function_">find</span>({}, <span class="hljs-keyword">function</span>(<span class="hljs-params">err, users</span>) {
    <span class="hljs-keyword">var</span> allUsers = users;
    <span class="hljs-keyword">var</span> adminUsers = [];
    <span class="hljs-keyword">var</span> corpUsers = [];
    <span class="hljs-keyword">var</span> techUsers = [];
    <span class="hljs-keyword">var</span> formalUsers = [];
    <span class="hljs-keyword">var</span> searchUsers = [];
    users.<span class="hljs-title function_">forEach</span>(<span class="hljs-keyword">function</span>(<span class="hljs-params">user</span>) {
      <span class="hljs-keyword">if</span>(user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span> &amp;&amp;
         (user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span>.<span class="hljs-property">corpUsers</span> ||
          user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span>.<span class="hljs-property">techUsers</span> ||
          user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span>.<span class="hljs-property">formalUsers</span> ||
          user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span>.<span class="hljs-property">searchUsers</span> ||
          user.<span class="hljs-property">permissions</span>.<span class="hljs-property">admin</span>.<span class="hljs-property">superAdmin</span>)) {
        adminUsers.<span class="hljs-title function_">push</span>(user);
      }
      <span class="hljs-keyword">if</span>(user.<span class="hljs-property">permissions</span>.<span class="hljs-property">general</span>.<span class="hljs-property">corpUsers</span>) {
        corpUsers.<span class="hljs-title function_">push</span>(user);
      }
      <span class="hljs-keyword">if</span>(user.<span class="hljs-property">permissions</span>.<span class="hljs-property">general</span>.<span class="hljs-property">formalUsers</span>) {
        formalUsers.<span class="hljs-title function_">push</span>(user);
      }
      <span class="hljs-keyword">if</span>(user.<span class="hljs-property">permissions</span>.<span class="hljs-property">general</span>.<span class="hljs-property">search</span>) {
        searchUsers.<span class="hljs-title function_">push</span>(user);
      }
      <span class="hljs-comment">// this continued on for another 40 lines or so...</span>
    })
  })
}
</code></pre>
<p>Node.js has <a href="https://nodejs.org/api/perf_hooks.html">performance tooling</a> built-in that allows you to gain insight around memory and CPU usage, which is what we used next:</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> { <span class="hljs-title class_">PerformanceObserver</span>, performance } = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;perf_hooks&#x27;</span>)
<span class="hljs-keyword">const</span> o = <span class="hljs-keyword">new</span> <span class="hljs-title class_">PerformanceObserver</span>(<span class="hljs-function">(<span class="hljs-params">items</span>) =&gt;</span> {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(items.<span class="hljs-title function_">getEntries</span>()[<span class="hljs-number">0</span>]);
  performance.<span class="hljs-title function_">clearMarks</span>();
})
o.<span class="hljs-title function_">observe</span>({ <span class="hljs-attr">entryTypes</span>: [<span class="hljs-string">&#x27;measure&#x27;</span>]})
</code></pre>
<pre><code class="hljs language-javascript"><span class="hljs-comment">// to get the approx mem usage you can add this log line:</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`
  The script uses ~
  <span class="hljs-subst">${process.memoryUsage().heapUsed / <span class="hljs-number">1024</span> / <span class="hljs-number">1024</span>}</span>
  MB
`</span>)
</code></pre>
<pre><code class="hljs language-shell"><span class="hljs-meta"># </span><span class="language-bash">Output</span>
PerformanceEntry {
  name: &#x27;getUsers to res.status(200)&#x27;,
  entryType: &#x27;measure&#x27;,
  startTime: 5486.524808,
  duration: 6559.275542
}
The script uses ~ 456.143798828125 MB
</code></pre>
<p>Looking at this code I couldn’t help but wonder if there was a more efficient way to query and aggregate this information.</p>
<h2 id="fundamental-3-identify-the-missing-fundamental">Fundamental #3: Identify the Missing Fundamental</h2>
<p>Returning to the idea of the missing fundamental, as audio engineers must ask themselves “what can I change about the frequencies in this mix in order to bring things into harmony?” the relevant question for software engineers is very similar: “what does this system need in order to bring harmony to its operation?”. In our case it was also helpful to consider that question in a historical context as “what fundamental were the original developers missing when they built this?”</p>
<p>In both cases, the answer for this application was <strong>how to query things more efficiently</strong>!</p>
<p>The <code>getUsers</code> method above was doing two things wrong; querying inefficiently for <em>all</em> the users in the system and then allocating large arrays to partition the data based on permissions. Once we understood the missing fundamental we had a path forward to try and optimize this poorly performing code: we should see if we can query things more efficiently. This is what we came up with using <code>async/await</code> and the MongoDB <a href="https://docs.mongodb.com/manual/aggregation/">aggregation pipeline</a>:</p>
<pre><code class="hljs language-javascript"><span class="hljs-attr">getUsers</span>: <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span>(<span class="hljs-params">req, res</span>) {
  <span class="hljs-keyword">const</span> adminUsers = <span class="hljs-keyword">await</span> <span class="hljs-title class_">User</span>.<span class="hljs-title function_">aggregate</span>([
    {
      <span class="hljs-attr">$match</span>: {
        <span class="hljs-attr">$or</span>: [
          { <span class="hljs-string">&quot;permissions.admin.corpUsers&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }},
          { <span class="hljs-string">&quot;permissions.admin.techUsers&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }},
          { <span class="hljs-string">&quot;permissions.admin.formalUsers&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }},
          { <span class="hljs-string">&quot;permissions.admin.searchUser&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }},
          { <span class="hljs-string">&quot;permissions.admin.superAdmin&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }},
        ]
      }
    }
  ])

  <span class="hljs-keyword">const</span> corpUsers = <span class="hljs-keyword">await</span> <span class="hljs-title class_">User</span>.<span class="hljs-title function_">aggregate</span>([
    {
      <span class="hljs-attr">$match</span>: {
        <span class="hljs-string">&quot;permissions.general.corpUsers&quot;</span> : { <span class="hljs-attr">$eq</span>: <span class="hljs-literal">true</span> }
      }
    }
  ])

  <span class="hljs-comment">// ... etc...</span>

  res.<span class="hljs-title function_">status</span>(<span class="hljs-number">200</span>).<span class="hljs-title function_">json</span>({
    <span class="hljs-attr">data</span>: {
      adminUsers,
      corpUsers,
      ...
    }
  })
}
</code></pre>
<p>Once we had re-written and tested the query to make sure the output was the same, we re-ran our performance profiling to see what the difference was.</p>
<pre><code class="hljs language-shell">PerformanceEntry {
  name: &#x27;getUsers to res.status(200)&#x27;,
  entryType: &#x27;measure&#x27;,
  startTime: 496079.094306,
  duration: 270.1256
}
The script uses ~ 44.550048828125 MB
</code></pre>
<p>Using the Aggregation pipeline had yielded an order of magnitude less memory and CPU usage! Here’s a couple of screenshots from the Heroku dashboard for this app that show the before/after comparisons as well.</p>
<h3 id="before---request-timeouts-and-high-memory-usage">Before - request timeouts and high memory usage.</h3>
<p><img src="/img/the-missing-fundamental/before-30s-timeouts-large-ram-usage.png" alt="Before"></p>
<h3 id="after---no-timeouts-memory-usage-reduced-by-a-factor-of-10">After - no timeouts, memory usage reduced by a factor of 10.</h3>
<p><img src="/img/the-missing-fundamental/after-2s-no-timeouts-50mb-ram-usage.png" alt="After"></p>
<h1 id="conclusions">Conclusions</h1>
<p>If you find yourself in a similar situation evaluating the performance of some legacy code I would encourage you to think about asking questions around what the missing fundamental(s) are. Walking through <strong>investigate</strong>, <strong>profile</strong>, <strong>identify</strong> (rinse. repeat.) has been useful for me, and I hope it is for you!</p>
]]></description><link>https://blog.davemo.com/posts/2020-03-10-the-missing-fundamental.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2020-03-10-the-missing-fundamental.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate></item><item><title><![CDATA[CSS: The Visual State-Machine]]></title><description><![CDATA[<aside class="tldr">Using Svelte, we can reimagine the presentation layer of a web application as a state machine.</aside>

<p>Thinking of web applications in terms of <a href="https://en.wikipedia.org/wiki/Finite-state_machine">state machines</a> is <a href="https://www.techrepublic.com/article/set-up-web-applications-as-finite-state-machines/">not a new idea</a>; in fact, it has become so popular in the past few years that teams are spending increasingly more time breaking down their application into states managed by front-end frameworks.</p>
<p>Whether you use <a href="https://redux.js.org/">Redux</a>, <a href="https://mobx.js.org">MobX</a>, or even perhaps something framework-agnostic like <a href="https://xstate.js.org">xState</a>, it is clear that thinking about web applications in terms of state machines is occurring much more frequently. With all this focus on state, transitions, and the benefits that come with structuring our applications like this, I’ve found there is still an area that is often overlooked when it comes to managing state in web applications: <strong>the visual or presentation layer</strong>.</p>
<p>CSS is incredibly powerful yet frequently misunderstood by most developers, which often leads to derision of the language. I think this is mostly due to a fundamental error in the way web developers manage presentation, often focusing their efforts on conditional logic in templates instead of a more flexible application of state-specific CSS selectors to HTML elements.</p>
<iframe src="https://www.youtube.com/embed/xpnmtkjCNng?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<h2 id="a-simple-example">A Simple Example</h2>
<p>Let us examine a simple example of a multi-selectable list for a user interface (UI) that a designer may have provided in mockup form for us as web developers to decompose into working code.</p>
<p><img src="/img/css-the-visual-state-machine/css-mockup.gif" alt="An animation of a multi-selectable list of users in tabular form. Each row has an icon which changes to indicate the selection state, defaulting to a snowman, empty checkbox on hover, and finally checked checkbox when selected."></p>
<p>We can see there are a number of interactions at play, and at first glance these might seem simple enough that we would be tempted to solve the problem without putting much upfront thought into it. However, I think despite the simplicity of the example, there are enough complex states to enumerate that we should spend some time thinking about them before we dive into creating this UI.</p>
<table>
<thead>
<tr>
<th>State</th>
<th>Trigger</th>
<th>Change</th>
</tr>
</thead>
<tbody><tr>
<td><em>Selected</em></td>
<td>Click</td>
<td>Icon changes to a checkbox</td>
</tr>
<tr>
<td><em>Unselected</em></td>
<td>Click</td>
<td>Icon changes to an empty box</td>
</tr>
<tr>
<td><em>Hovering, No Selections</em></td>
<td>Hover</td>
<td>The snowman icon for the hovered row changes to an empty checkbox to indicate potential for selection</td>
</tr>
<tr>
<td><em>Hovering, 1 or More Selections</em></td>
<td>Hover</td>
<td>All unselected row icons remain as empty checkboxes, and a yellow highlight appears on the hovered row</td>
</tr>
<tr>
<td><em>1 or more Selections Active</em></td>
<td>No User Interaction</td>
<td>All icons change to empty checkboxes to indicate the ability to select multiple rows</td>
</tr>
</tbody></table>
<p>Putting aside <a href="https://en.wikipedia.org/wiki/Interaction_design">interaction design</a> (IxD) and <a href="https://en.wikipedia.org/wiki/Web_accessibility">accessibility</a> (a11y) concerns for the time being, after enumerating the states that we see here there is a lot to consider when building this UI! How should we manage the states? Should the logic live in our template or in our stylesheets? Let’s take a brief look at the first approach, using an implementation in <a href="https://svelte.dev">Svelte</a>.</p>
<p>Svelte is a compiler that takes as input one or more <code>.svelte</code> files with <em>regions</em> of functionality based on JavaScript, HTML, and CSS; with that input it produces the minimal amount of DOM API output in JavaScript to achieve the desired result. It’s a different take than something like React, Angular, or Ember, which ship substantial runtimes to the browser that execute application code. If you are interested in learning more I highly recommend watching this excellent talk called <em><a href="https://www.youtube.com/watch?v=AdNJ3fydeao">Rethinking Reactivity</a></em> from Rich Harris introducing some of the core ideas. The code in the following examples is intended to be simple enough that you should be able to port the ideas represented to any other framework with minimal effort.</p>
<h2 id="implementation-templates">Implementation: Templates</h2>
<p>One of the first places a web developer might start is by crafting the template that represents the UI mockup we received from our designer friend above. This seems like a logical place to start, given we need some way to represent the data in a web browser. Let’s build a template using svelte-infused HTML and see how it looks.</p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>
  {#each users as user}
  <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;icon&quot;</span>&gt;</span>
      {#if user.selected &amp;&amp; hasSelection}
        {checkedBox}
      {:else if !user.selected &amp;&amp; hasSelection}
        {uncheckedBox}
      {:else}
        {snowman}
      {/if}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.email}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  {/each}
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<blockquote>
<p>Aside from the svelte-specific things like the <code>{#each}</code> and <code>{#if}</code> blocks, this is probably close to what you might implement in any front-end or server-side templating solution.</p>
</blockquote>
<p>We’ve taken the list of potential states that we extracted from the mockup above and encoded them as conditional logic in our templates in order to achieve the desired result. The one special case we needed to account for was the non-interactive state “1 or more Selections Active”; to do this we defined a local variable in our JavaScript region called <code>hasSelection</code> which is defined using Sveltes <a href="https://svelte.dev/tutorial/reactive-declarations">reactive declarations</a> as</p>
<pre><code class="hljs language-javascript"><span class="hljs-attr">$</span>: hasSelection = users.<span class="hljs-title function_">some</span>(<span class="hljs-function"><span class="hljs-params">u</span> =&gt;</span> u.<span class="hljs-property">selected</span>)
</code></pre>
<p>Although the code above satisfies <em>most</em> of the user experience (UX) as detailed in the mockup, there are two problems that shake out of an implementation like this that focuses on conditional logic in templates:</p>
<ol>
<li>We didn’t capture <em>all</em> of the states enumerated, as we cannot effectively translate a user’s <code>hover</code> action in templates alone unless we get really creative and complex</li>
<li>This paradigm scales <em>very poorly</em> as our templates grow, mixing concerns of <code>presentation</code> and <code>data</code> in a template, resulting in code that is much harder to read and maintain over the life of a project</li>
</ol>
<p>The scalability concern is the more worrisome of the two, yet is a common byproduct of developers using conditional logic in templates. Increasingly thorny conditionals can lead to missed acceptance criteria, which in turn can lead to stress and tension on a team. Rather than throw blame around, it’s worth focusing on whether that approach is healthy for a long-term project.</p>
<p>I think we can do better if we shift our focus from conditional logic in templates to thinking more in terms of leveraging CSS as the language we use to define the states in our presentational state machine and using JavaScript to manage when to apply those states. Let’s see what that looks like as we refactor the above example.</p>
<h2 id="implementation-stylesheets">Implementation: Stylesheets</h2>
<p>One of the first considerations we’ll need to make is how to address both the concerns raised in the previous section. We need to handle the <code>hover</code> state properly, and we also should strive for a solution that encodes data in the template and presentation in the stylesheets. Let’s start by refactoring the template to eliminate the conditional logic:</p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class:hasSelection</span>=<span class="hljs-string">&quot;{hasSelection}&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;selectable&quot;</span>&gt;</span>
  {#each users as user}
  <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">class:selected</span>=<span class="hljs-string">&quot;{user.selected}&quot;</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;icon&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.email}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  {/each}
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<p>The first thing you might notice is that we removed the conditional blocks replaced them with <a href="https://svelte.dev/docs#class_name">svelte’s class element directive</a>. This is an elegant way to control toggling of a CSS class on an element via a boolean value, which we previously defined as <code>{user.selected}</code> and <code>{hasSelection}</code>. We also added a <code>class=selectable</code> to the root table element in order to allow us to better manage the complexity of the conditional logic for states in CSS. Let’s defer looking at the JavaScript that defines those values and instead look at what the definition of each state in our presentational state machine looks like when we encode it with CSS:</p>
<pre><code class="hljs language-css"><span class="hljs-comment">/*
  CSS variables in conjunction with escaped unicode or html
  entities are a great way to represent things like icons
*/</span>
<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attr">--unchecked-box</span>: <span class="hljs-string">&quot;\02610&quot;</span>;
  <span class="hljs-attr">--checked-box</span>: <span class="hljs-string">&quot;\02611&quot;</span>;
  <span class="hljs-attr">--snowman</span>: <span class="hljs-string">&quot;\02603&quot;</span>;
}

<span class="hljs-comment">/*
  Managing the hover states to show a yellow background
*/</span>
<span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span>,
<span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-tag">td</span> {
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">background-color</span>: yellow;
}

<span class="hljs-comment">/*
  Our first state, every icon should default to the snowman
*/</span>
<span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
  <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--snowman);
}

<span class="hljs-comment">/*
  A complex state, if the table has a selection,
  then every selected items icon should be the checked box
*/</span>
<span class="hljs-selector-class">.selectable</span><span class="hljs-selector-class">.hasSelection</span> <span class="hljs-selector-class">.selected</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
  <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--checked-box);
}

<span class="hljs-comment">/*
  A combined selector to handle the alternative complex states:
  - for a table without a selection, when the user hovers, show the unchecked box
  - for a table with selections, swap the icon from the snowman to the unchecked box
*/</span>
<span class="hljs-selector-class">.selectable</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-class">.hasSelection</span>) <span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span>,
<span class="hljs-selector-class">.selectable</span><span class="hljs-selector-class">.hasSelection</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
  <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--unchecked-box);
}
</code></pre>
<p>With the combination of CSS and svelte-infused HTML we’ve achieved the result our designer was hoping for when they handed us the initial mockup, with an appropriate separation between the definition of our states (CSS) and the application of those states (HTML, and JavaScript).</p>
<p>For completeness, here is the entirety of the example as included in <code>Application.svelte</code> from the <a href="https://github.com/davemo/svelte-casts">code on github</a>:</p>
<pre><code class="hljs language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="language-javascript">
  <span class="hljs-keyword">let</span> users = [
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Danika Dywtgowm&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;danika.dywtgowm@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Erica Bule&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;erica.bule@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Jim Snales&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;jim.snales@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Daria Thorobox&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;daria.thorobox@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Mendikant Hargrove&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;mendikant.hargrove@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Ephraim Lischok&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;ephraim.lischok@email.com&#x27;</span>},
    {<span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;Lera Nedialkova&#x27;</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">&#x27;lera.nedialkova@email.com&#x27;</span>},
  ]

  <span class="hljs-keyword">function</span> <span class="hljs-title function_">selectUser</span>(<span class="hljs-params">user</span>) {
    users[users.<span class="hljs-title function_">findIndex</span>(<span class="hljs-function"><span class="hljs-params">u</span> =&gt;</span> u.<span class="hljs-property">name</span> === user.<span class="hljs-property">name</span>)] = {
      ...user,
      <span class="hljs-attr">selected</span>: !user.<span class="hljs-property">selected</span>
    }
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`<span class="hljs-subst">${user.name}</span> was <span class="hljs-subst">${user.selected ? <span class="hljs-string">&#x27;de-selected&#x27;</span> : <span class="hljs-string">&#x27;selected&#x27;</span>}</span>`</span>);
  }

  <span class="hljs-attr">$</span>: hasSelection = users.<span class="hljs-title function_">some</span>(<span class="hljs-function"><span class="hljs-params">u</span> =&gt;</span> u.<span class="hljs-property">selected</span>)
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
  <span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attr">--unchecked-box</span>: <span class="hljs-string">&#x27;\02610&#x27;</span>;
    <span class="hljs-attr">--checked-box</span>: <span class="hljs-string">&#x27;\02611&#x27;</span>;
    <span class="hljs-attr">--snowman</span>: <span class="hljs-string">&#x27;\02603&#x27;</span>;
  }

  <span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>;
  }

  <span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-tag">td</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">background-color</span>: yellow;
  }

  <span class="hljs-selector-class">.icon</span>, <span class="hljs-selector-class">.template-icon</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
  }

  <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
    <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--snowman);
  }

  <span class="hljs-selector-class">.selectable</span><span class="hljs-selector-class">.hasSelection</span> <span class="hljs-selector-class">.selected</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
    <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--checked-box);
  }

  <span class="hljs-selector-class">.selectable</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-class">.hasSelection</span>) <span class="hljs-selector-tag">tr</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span>,
  <span class="hljs-selector-class">.selectable</span><span class="hljs-selector-class">.hasSelection</span> <span class="hljs-selector-class">.icon</span><span class="hljs-selector-pseudo">:after</span> {
    <span class="hljs-attribute">content</span>: <span class="hljs-built_in">var</span>(--unchecked-box);
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Complex Multi-Select<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">cellspacing</span>=<span class="hljs-string">0</span> <span class="hljs-attr">class:hasSelection</span>=<span class="hljs-string">{hasSelection}</span> <span class="hljs-attr">class</span>=<span class="hljs-string">selectable</span>&gt;</span>
  {#each users as user}
  <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">class:selected</span>=<span class="hljs-string">{user.selected}</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> selectUser(user)}&gt;
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">icon</span> <span class="hljs-attr">height</span>=<span class="hljs-string">20</span> <span class="hljs-attr">width</span>=<span class="hljs-string">20</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
      {user.email}
    <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  {/each}
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>This is how I have tended to manage the working relationship between HTML and CSS for the last 20 years, and I think the power of thinking in this way leads to cleaner code and easier to refactor web interfaces.</p>
<p>If this looks completely foreign to you and you found yourself considering that the template-based conditional-logic approach made more sense, I’d recommend learning more about the capabilities of CSS features like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var">pseudo-selectors :not</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var">variables</a>, and the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/content">generated content: property</a>.</p>
<p>I’ve found that teams who up their level of knowledge in CSS and tend to try to split concerns like we’ve done here will have web applications that are easier to change over the long term.</p>
<p>If you are interested in learning more about this approach and seeing a live coded version of this blog post, please check out the <a href="https://www.youtube.com/watch?v=xpnmtkjCNng">screencast</a> posted to my YouTube channel; it walks through all the examples and touches on a few more svelte-specific things to consider.</p>
<h2 id="learning-resources">Learning Resources</h2>
<ul>
<li><a href="https://svelte.dev/tutorial/basics">Svelte Tutorial</a></li>
<li><a href="https://github.com/davemo/svelte-casts">Complex-Multi-Select Code on Github</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var">CSS Variables</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not">CSS :not pseudo-selector</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/content">CSS Generated Content</a></li>
<li><a href="https://www.toptal.com/designers/htmlarrows/symbols/">HTML Entity Symbols</a></li>
</ul>
]]></description><link>https://blog.davemo.com/posts/2019-08-27-css-the-visual-state-machine.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2019-08-27-css-the-visual-state-machine.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Tue, 27 Aug 2019 00:00:00 GMT</pubDate></item><item><title><![CDATA[Open Source Spotlight: Dependable JS]]></title><description><![CDATA[<aside class="tldr">
<a href="https://github.com/testdouble/dependable">Dependable</a> is a dependency injection framework for Node.js
</aside>

<p>Recently, <a href="https://twitter.com/Schoonology">Michael Schoonmaker</a>, <a href="https://twitter.com/primarilysnark">Joshua Starkey</a>, and <a href="https://twitter.com/dmosher">myself</a> got together to brainstorm some improvements we wanted to make to an open source library called Dependable that we had used on a client project.</p>
<p><a href="https://github.com/testdouble/dependable">Dependable</a> is billed as “A minimalist dependency injection framework for node.js”, but I feel like it only took on the “minimalist” moniker after we shipped version 2.0 just a few weeks ago. As we sat down to discuss what we wanted to do there were a number of questions that shook out that I feel need to be asked by any team working on an open source project:</p>
<ul>
<li>How can we make this smaller?</li>
<li>What features are core and what can we prune?</li>
<li>How can we write the test-suite in a way that demonstrates real-world examples?</li>
<li>What should the README communicate?</li>
<li>How are we going to maintain this going forward?</li>
</ul>
<p>As we worked towards the backlog of features, we built up a list of <em>nice-to-haves</em> and <em>must-haves</em>, choosing to defer the former and focus our efforts on the latter. <a href="https://github.com/testdouble/dependable/projects/1">Github Projects</a> actually worked pretty well for a lightweight project management tool.</p>
<p><img src="/img/open-source-spotlight-dependable-js/github-projects-dependable.png" alt="Github Projects"></p>
<h2 id="limiting-api-surface-area">Limiting API Surface Area</h2>
<p>One of our stated goals was to reduce the surface area of dependable in order to eliminate complexity in the codebase. The 1.0 release of dependable included a public API with 6 methods on the dependency inversion <code>container</code> and our rewrite whittled this down to 4. Choosing to have this discussion early allowed us to focus on what the most valuable parts of the API were, using our experience of real-world use within consulting projects to help guide us.</p>
<table>
<thead>
<tr>
<th>1.0 Public API</th>
<th>2.0 Public API</th>
</tr>
</thead>
<tbody><tr>
<td><code>container.register(name, func)</code></td>
<td><code>container.factory(name, func)</code></td>
</tr>
<tr>
<td><code>container.register(hash)</code></td>
<td><code>container.constant(name, object)</code></td>
</tr>
<tr>
<td><code>container.load(fileOrFolder)</code></td>
<td>removed</td>
</tr>
<tr>
<td><code>container.get(name, overrides = {})</code></td>
<td><code>container.get(name, overrides = {})</code></td>
</tr>
<tr>
<td><code>container.resolve(overrides={}, cb)</code></td>
<td>removed</td>
</tr>
<tr>
<td><code>container.list()</code></td>
<td>removed</td>
</tr>
<tr>
<td>non-existent</td>
<td><code>container.getSandboxed(name, overrides={})</code></td>
</tr>
</tbody></table>
<p>With the API sufficiently whittled down, we also set ourselves to renaming the methods in the public API in order to better reveal the intent for each method and avoid violating <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">SRP</a> (which some of the 1.0 API methods had done via overloading). <code>container.register(hash)</code> became <code>container.constant(name, object)</code>, and <code>container.register</code> was renamed simply to <code>container.factory</code>. We deliberated for a while over naming but felt that <code>.factory</code> and <code>.constant</code> were terms that were familiar enough in the context of dependency injection and more descriptive than their 1.0 counterparts.</p>
<h2 id="rewriting-source-and-test">Rewriting Source and Test</h2>
<p>Dependable 1.0 was written in CoffeeScript which we felt would limit options for potential future contributors. We chose to rewrite the library in ES6 and use <a href="https://standardjs.com/">standard</a> to manage formatting the code for us. It becomes much easier for future contributors to submit patches when the tooling in an open source project handles formatting of the code.</p>
<table>
<thead>
<tr>
<th>1.0 LOC (index.coffee)</th>
<th>2.0 LOC (index.js)</th>
</tr>
</thead>
<tbody><tr>
<td>134</td>
<td>99</td>
</tr>
</tbody></table>
<p>The test-suite took slightly longer to rewrite due to the removal of <code>container.load(fileOrFolder)</code>. We felt that this method complected the 1.0 codebase and was syntactic sugar for what could be accomplished by a user via loading one or many files externally using <code>require</code> or <code>import</code> and then invoking <code>container.factory(name, func)</code> within the context of a call to <code>.map</code>. In addition to translating the <code>.coffee</code> test sources to <code>.js</code>, we also reorganized the test examples themselves to better communicate the intended use of dependable.</p>
<table>
<thead>
<tr>
<th>1.0 Test LOC (multiple .coffee files)</th>
<th>2.0 Test LOC (test.js)</th>
</tr>
</thead>
<tbody><tr>
<td>~380</td>
<td>259</td>
</tr>
</tbody></table>
<p>A consistent use of objects from a real world scenario involving a <code>Logger</code>, <code>Router</code>, and <code>Formatter</code> in the context of an <code>App</code> was used throughout the test-suite. I’ve found that I’m much more likely to contribute to open source projects that have a well architected test suite with meaningful examples.</p>
<pre><code class="hljs language-javascript"><span class="hljs-title function_">it</span>(<span class="hljs-string">&#x27;should register a factory with a single dependency&#x27;</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
  subject.<span class="hljs-title function_">factory</span>(<span class="hljs-string">&#x27;logger&#x27;</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;message&#x27;</span>
  })
  subject.<span class="hljs-title function_">factory</span>(<span class="hljs-string">&#x27;app&#x27;</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">logger</span>) {
    <span class="hljs-keyword">return</span> logger
  })
  assert.<span class="hljs-title function_">equal</span>(subject.<span class="hljs-title function_">get</span>(<span class="hljs-string">&#x27;app&#x27;</span>), <span class="hljs-string">&#x27;message&#x27;</span>)
})
</code></pre>
<p>In addition, we added first-class support for isolation testing via <code>container.getSandboxed</code> which should be used during testing to ensure that a module under test has been completely isolated.</p>
<h2 id="closing-thoughts--recommended-reading">Closing Thoughts &amp; Recommended Reading</h2>
<p>At Test Double we are proud of our commitment to open source and we take pride in trying to be thoughtful in the way we approach open source stewardship. If you share these values and are interested in joining us you should <a href="https://testdouble.com/join/">reach out</a>, we’re hiring!</p>
<p>If you aren’t familiar with the concept or benefits of Dependency Injection these are some great followup resources to get you thinking:</p>
<ul>
<li><a href="https://martinfowler.com/articles/injection.html">Inversion of Control Containers and the Dependency Injection pattern</a></li>
<li><a href="https://www.youtube.com/watch?v=mU1JcPikdMs">Inversion of Control, The UI Thread and Backbone.JS Views</a></li>
</ul>
]]></description><link>https://blog.davemo.com/posts/2018-04-06-open-source-spotlight-dependable-js.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2018-04-06-open-source-spotlight-dependable-js.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 06 Apr 2018 00:00:00 GMT</pubDate></item><item><title><![CDATA[The Consultants Code]]></title><description><![CDATA[<aside class="tldr">
  As software-consultants, building trust should be our number one goal, but we have to remember that building trust takes time.
</aside>

<blockquote>
<p>I wish we could have built it right the first time but we were learning along the way.</p>
<p>Ugh, we got handed this mess and now we have to figure out what to do with it.</p>
<p>What do you think we should do?</p>
</blockquote>
<p>Sound familiar? If any of these statements resonates with you then you’ve most likely been in the shoes of the person making the statement <em>or</em> as a consultant engaged with a client in a similar predicament. The phrase “Software Consulting” generally evokes feelings of <strong>praise</strong> or <strong>disdain</strong>, or perhaps both depending on the circumstances. As consultants, we are often positioned as experts and typically engaged during times of crisis or uncertainty; so it should come as no surprise that our ability to deliver in the midst of that environment is the primary measure of our effectiveness.</p>
<p>Expectations about what we are hired to deliver generally set the scale by which we are judged; frequently that involves analysis, reporting, recommendations but also training, mentoring, and code. The world is a big enough place that it’s plausible to think of a scenario where the opening statements above were made by someone dealing with the aftermath of a poor experience with a previous software consultancy, typically measured by the state or quality of “the code”.</p>
<p>While it would be interesting to attempt an objective look at what typifies “quality” as it relates to “code”, I think there is a <em>more</em> interesting vector to approach the quality valuation that also pertains to “code” of a different kind; call it the code of conduct, or the way we interact with our clients as consultants, or perhaps even our ability to relate and help them rise out of the midst of chaos and uncertainty&mdash;this type of consultant’s code is less frequently explored, which is a shame, because I firmly believe it has a more direct impact on the physical code we write than our technical expertise.</p>
<h2 id="hostile-takeover">Hostile Takeover</h2>
<p>Consultants in any industry are often vilified from the start of an engagement due to a perceived “us vs them” mentality. Depending on the how the engagement was initiated, trust was likely extended from key decision makers but generally does <em>not</em> extend from the rank and file employees of the company. This puts an immediate burden of responsibility on the consultant to become adept at earning that trust inclusively and at discerning barriers which would prevent that from happening.</p>
<p>The key to building this trust starts with establishing relationships with <em>everyone</em> on the team. It might be easy to think of ourselves as only accountable to the primary stakeholders, but unless we earn the trust of our peers and treat them as equals, it will be challenging to avoid the perception of a hostile takeover. In essence we should think of ourselves as partners, invested in the success of the company and each member of the team. Finding opportunities to mentor team members and attribute wins to others is a great way foster a positive consulting environment.</p>
<h2 id="cooperation--commiseration">Cooperation &amp; Commiseration</h2>
<p>At some point any consulting engagement will involve hearing about the technical failures, company problems, personnel issues, and every other negative thing under the sun; people like to complain. Good consultants know how to view these conversations as opportunities to build trust without becoming embroiled in corporate politics. It is a balancing act to be able to participate in just enough commiseration to demonstrate understanding without fostering a negative cycle. I believe a critical component to developing this skill is the ability to hone one’s capacity for <a href="https://en.wikipedia.org/wiki/Active_listening">active listening</a>.</p>
<p>There is a fine line between commiserating and ruminating, and good consultants know how to empathize and then steer the conversation towards solutions instead of ruminating on past failures.</p>
<h2 id="technical-overdrive">Technical Overdrive</h2>
<p>Depending on the nature of the team and the consulting engagement, it may prove more effective to lead by example and jump into what I like to call “technical overdrive”. I’ve seen many teams become derailed in endless technical round-table discussions and talk themselves into a corner before ever trying to code up a prototype or draft solution. In these scenarios I have found it best to move forward with quick prototypes of solutions proposed by the team, enabling them to have more concrete discussion points and base decisions on actual implementations. Working code and a good demo can unblock many teams.</p>
<p>Like every piece of advice, when to jump into technical overdrive is highly context-sensitive; sometimes it is more appropriate to employ the opposite strategy and slow things down. Some teams have a bad habit of shipping their prototypes to production, and it is in this scenario where good consultants will try and introduce better habits around technical design, architecture discussions and how to effectively prototype. In some ways the slow-things-down scenario also involves technical overdrive, as it might mean taking the lead on demonstrating what these good habits look like for the team.</p>
<h2 id="closing-thoughts--recommended-reading">Closing Thoughts &amp; Recommended Reading</h2>
<p>As a consultant, building trust should be our number one goal, but we have to remember that it takes time and is a skill that needs to be cultivated over the course of many engagements. If you are interested in spending time honing your skills and improving your “consultant’s code” I would recommend investing in the following resources:</p>
<ul>
<li><a href="https://www.amazon.ca/Secrets-Consulting-Giving-Getting-Successfully/dp/0932633013">The Secrets of Consulting: A Guide to Giving and Getting Advice Successfully</a></li>
<li><a href="https://www.amazon.com/More-Secrets-Consulting-Consultants-Tool-ebook/dp/B004J35LH6">More Secrets of Consulting: The Consultant’s Tool Kit</a></li>
<li><a href="https://www.amazon.ca/Crucial-Conversations-Talking-Stakes-Second/dp/0071771328">Crucial Conversations: Tools for Talking When Stakes Are High</a></li>
<li><a href="https://www.amazon.com/Influencer-Science-Leading-Change-Second/dp/0071808868">Influencer: The New Science of Leading Change</a></li>
<li><a href="https://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034">How to Win Friends &amp; Influence People</a></li>
<li><a href="https://www.amazon.ca/SPIN-Selling-Neil-Rackham/dp/0070511136">Spin Selling</a></li>
</ul>
<p>Also, if you’re looking for a great opportunity to develop your consulting skills, they are hiring at Test Double, you should <a href="https://testdouble.com/join/">apply</a>. :)</p>
]]></description><link>https://blog.davemo.com/posts/2018-02-01-the-consultants-code.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2018-02-01-the-consultants-code.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Thu, 01 Feb 2018 00:00:00 GMT</pubDate></item><item><title><![CDATA[React Performance Analysis]]></title><description><![CDATA[<aside class="tldr">
  A screencast that covers a number of techniques on evaluating the performance of React applications.
</aside>

<p>React is frequently touted as being performant due to the optimizations of its Virtual DOM technique, yet all to often this is used by developers as a crutch to avoid thinking about the performance of their code <em>at all</em>. This generally leads to performance problems in React apps of any significant scale.</p>
<p>With the proliferation of React applications in the wild, I thought it would be a good idea to examine some techniques for evaluating the performance of React Components.</p>
<p>This screencast covers a number of techniques for constructing components, but also shows how to evaluate performance objectively and make informed refactoring decisions.</p>
<iframe src="https://www.youtube.com/embed/sVDnCMIkmTM?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<h2 id="screencast-outline">Screencast Outline</h2>
<ol>
<li>Introduction</li>
</ol>
<ul>
<li>A Common UI Scenario for React Components -&gt; Large Data Tables</li>
<li>Developer Default: Google/Stack Overflow Driven Development</li>
<li>The Quest for a pre-built Library</li>
</ul>
<ol start="2">
<li>Performance Goals</li>
</ol>
<ul>
<li>Important Metrics: TTI (time to interactive), TTFMP (time to first meaningful paint)</li>
<li>Setting a Frame Budget: (60fps / 16.7ms) might not always be feasible</li>
<li>The Feedback Loop: Experiment, Evaluate</li>
</ul>
<ol start="3">
<li>React Development Patterns</li>
</ol>
<ul>
<li>Start BIG: 1 component, 1 render method, then profile</li>
<li>Decompose: limit the surface area of your component by thinking hard about props, then profile</li>
<li>Optimize by:<ul>
<li>Reducing JS Execution: work the browser doesn’t have to do doesn’t need to be optimized</li>
<li>Reducing Surface Area for Change: small components, limited surface area, small lists of props</li>
<li>Keep <code>render</code> methods simple and <em>mostly</em> static</li>
</ul>
</li>
</ul>
<h2 id="code">Code</h2>
<p><a href="https://github.com/davemo/react-performance-analysis">https://github.com/davemo/react-performance-analysis</a></p>
]]></description><link>https://blog.davemo.com/posts/2017-11-07-react-performance-analysis.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2017-11-07-react-performance-analysis.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Tue, 07 Nov 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Makefile Usability Tips]]></title><description><![CDATA[<aside class="tldr">
You can improve the DX of Makefiles using some simple target annotations and some parsing with <code>grep</code> and <code>awk</code>.
</aside>

<p>It has been but a <a href="/build-automation-holy-war.png">few short years</a> since <em>web developers</em> chose a side and took up arms in the holy war of <a href="https://en.wikipedia.org/wiki/Make_(software)">build automation tools</a>; this is only one of <a href="https://en.wikipedia.org/wiki/Editor_war">many</a> <a href="https://en.wikipedia.org/wiki/Browser_wars">wars</a> that have been fought <a href="https://en.wikipedia.org/wiki/Indent_style">countless times</a> since the dawn of computing. In the grim darkness of the far future, there is only war.</p>
<p>Meh. War is tiresome and I have had enough of war. This post is about some small usability improvements you can add to your Makefiles if you are using Make. Let’s dig in!</p>
<h2 id="the-makefile">The Makefile</h2>
<p>Make has been around for a <a href="https://en.wikipedia.org/wiki/Make_(software)">long time</a>. It has some neat features but it’s not always the friendliest to neophytes. Imagine you have a new developer joining your team, and your project has a Makefile that looks something like this</p>
<pre><code class="hljs language-Makefile">VERSION ?= <span class="hljs-variable">$(<span class="hljs-built_in">shell</span> cat VERSION)</span>

<span class="hljs-meta"><span class="hljs-keyword">.PHONY</span>: version clean bump release</span>

<span class="hljs-section">build:</span>
  @echo <span class="hljs-string">&quot;building...&quot;</span>
  <span class="hljs-comment"># build the app here</span>

<span class="hljs-section">clean:</span>
  <span class="hljs-comment"># rm -rf build</span>

<span class="hljs-section">release:</span>
  <span class="hljs-comment"># bump</span>
  <span class="hljs-comment"># make push -e VERSION=$(shell cat VERSION)</span>

<span class="hljs-section">push:</span>
  <span class="hljs-comment"># push the build artifact at a given version somewhere</span>

<span class="hljs-section">version:</span>
  <span class="hljs-comment"># cat VERSION</span>

<span class="hljs-section">bump:</span>
  <span class="hljs-comment"># using semver, bump the version by a major, minor or patch increment</span>
</code></pre>
<p>At first glance this Makefile isn’t all that complicated but chances are your build automation process is composed of many more lines of code or even split into multiple places. The wise aged veteran developer on your team tells the new member “to build this project just clone this repo and run <code>make</code>“ after which the new developer sees</p>
<pre><code class="hljs language-shell">neophyte@newbie:~/code/project
<span class="hljs-meta">$ </span><span class="language-bash">make</span>
building...
</code></pre>
<p>Now, assuming new dev didn’t encounter any snags with project setup and installing dependencies (hah! unlikely) this still doesn’t present a great picture of how the project is assembled or the bits of the lifecycle that are involved at first glance. Let’s see if we can improve this initial Makefile developer experience.</p>
<h3 id="step-1-add-a-default_goal">Step 1: Add a DEFAULT_GOAL</h3>
<p>In Make semantics, <em>goals</em> are targets that <code>make</code> should strive to update. The docs give us a nice <a href="https://www.gnu.org/software/make/manual/html_node/Goals.html">explanation of goals</a> as well as some hints about how we can manage which goal is run first</p>
<blockquote>
<p>By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe. If the first rule in the makefile has several targets, only the first target in the rule becomes the default goal, not the whole list. You can manage the selection of the default goal from within your makefile using the .DEFAULT_GOAL variable (see <a href="https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#Special-Variables">Other Special Variables</a>).</p>
</blockquote>
<p>Even though the docs give us some informal conventions about the first target in our Makefile I think it <em>makes</em> (heh) for a better experience if we add some sort of help target that spits out some information to the terminal. Make doesn’t have any facility to display help messages like some <a href="https://rake.rubyforge.org/Rake/Application.html">other build automation tools</a>, but it won’t be too hard to add one. First, let’s add a default goal of help</p>
<pre><code class="hljs language-Makefile">.DEFAULT_GOAL := help

<span class="hljs-section">help:</span>
  @echo <span class="hljs-string">&quot;Welcome to the Project!&quot;</span>
</code></pre>
<caption>
  <strong>Tip:</strong> prefixing a line in your make target with `@` suppresses output of that line to stdout.
</caption>

<p>With this in place our new developer sees the following</p>
<pre><code class="hljs language-shell">neophyte@newbie:~/code/project
<span class="hljs-meta">$ </span><span class="language-bash">make</span>
Welcome to the Project!
</code></pre>
<p>Ok, this is a little more friendly but still not very useful. We can do better!</p>
<h3 id="step-2-annotate-makefile-targets">Step 2: Annotate Makefile Targets</h3>
<p>Let’s update our Makefile to add helpful annotations to <em>some</em> of our targets using comment blocks prefixed with <code>##</code></p>
<pre><code class="hljs language-Makefile"><span class="hljs-section">build: ## builds the application</span>
  @echo <span class="hljs-string">&quot;building...&quot;</span>

<span class="hljs-section">clean: ## gets you back to a clean working state</span>
  <span class="hljs-comment"># rm -rf build</span>

<span class="hljs-section">release: bump ## bump the VERSION file, git tags, and push to github</span>
  <span class="hljs-comment"># make push -e VERSION=$(shell cat VERSION)</span>
</code></pre>
<p>This annotation scheme works pretty well but doesn’t buy us anything on its own, to make this truly useful we need to parse the Makefile, look for lines prefixed with <code>##</code> and format them in a pretty way and write them to <code>stdout</code></p>
<h3 id="step-3-parse-annotations">Step 3: Parse Annotations</h3>
<p>Make includes a handy <a href="https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#Special-Variables">list of special variables</a> that can be used for all sorts of handy things. In this case we can use the <code>MAKEFILE_LIST</code> variable along with <code>grep</code>, <code>sort</code> and <code>awk</code> to get a list of annotated targets and display them on <code>stdout</code> in a user-friendly way</p>
<pre><code class="hljs language-Makefile"><span class="hljs-section">help:</span>
  @grep -E &#x27;^[a-zA-Z_-]+:.*?<span class="hljs-comment">## .*$$&#x27; $(MAKEFILE_LIST) | sort | awk &#x27;BEGIN {FS = &quot;:.*?## &quot;}; {printf &quot;\033[36m%-30s\033[0m %s\n&quot;, $$1, $$2}&#x27;</span>
</code></pre>
<blockquote>
<p>Wondering what the <code>help</code> target is doing? See this <a href="https://explainshell.com/explain?cmd=grep+-E+%27%5E%5Ba-zA-Z_-%5D%2B%3A.*%3F%23%23+.*%24%24%27+%24%28MAKEFILE_LIST%29+%7C+sort+%7C+awk+%27BEGIN+%7BFS+%3D+%22%3A.*%3F%23%23+%22%7D%3B+%7Bprintf+%22%5C033%5B36m%25-30s%5C033%5B0m+%25s%5Cn%22%2C+%24%241%2C+%24%242%7D%27">explain shell</a>.</p>
</blockquote>
<p>With that in place our new developer would run <code>make</code> from the command-line and see</p>
<pre><code class="hljs language-shell">neophyte@newbie:~/code/project
<span class="hljs-meta">$ </span><span class="language-bash">make</span>
build           builds the application
clean           gets you back to a clean working state
release         bump the VERSION file, git tags, and push to github
</code></pre>
<p>Yay! This is a much nicer developer-experience than what we started with. To take it even further I might suggest annotating only a subset of tasks that are most commonly used.</p>
<h2 id="acknowledgements--links">Acknowledgements / Links</h2>
<p>There are a few sources that come up when you google for “self-documenting makefile” along with a few different ways of solving this problem. I drew inspiration for this post mostly from <a href="https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html">this marmelab entry</a>, but felt like there were enough interesting points and general make tips added that it was worth another post.</p>
<p>Other links you may find useful:</p>
<ul>
<li><a href="https://gist.github.com/davemo/c0462e8196289e0fb0210ee63ff02962">Full Makefile as Gist</a></li>
<li><a href="https://gist.github.com/davemo/88de90577a57698dd72d722bcfc44964">VERSION bump script as Gist</a></li>
<li><a href="https://www.gnu.org/software/make/">GNU Make</a></li>
<li><a href="https://www.cmcrossroads.com/print/article/self-documenting-makefiles">Self Documenting Makefiles</a></li>
<li><a href="https://explainshell.com">Explain Shell</a></li>
</ul>
]]></description><link>https://blog.davemo.com/posts/2017-04-17-makefile-usability-tips.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2017-04-17-makefile-usability-tips.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Mon, 17 Apr 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Advanced Directives with AngularJS (Part 2)]]></title><description><![CDATA[<aside class="tldr">
A continuation of my exploration of AngularJS and advanced directives through a screencast.
</aside>

<p>Two years in the making; just released and fresh off the presses it’s yet another screencast covering everybodys favorite enterprise JS framework: Angular JS!</p>
<p>In all seriousness, I had a hard time considering whether or not to publish this screencast because I found myself questioning whether the content would still be relevant almost two years later. However, in revisiting all the comments and questions about the alluded-to “part 2” from the first video I felt like there were still valuable things to talk about. At the heart of this screencast is discussion around what I consider <strong>one of the most valuable features of angular</strong>: the ability to use custom elements as a domain-specific language (DSL) to ease the <strong>wrapping and use of 3rd party libraries</strong>.</p>
<iframe src="https://www.youtube.com/embed/4zG8SfucUzg?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<p>This screencast continues the examination of some of the advanced features in Angular from <a href="https://blog.davemo.com/posts/2015-02-13-advanced-directives-with-angular-js">Advanced Directives with Angular JS</a> and expands by tackling some of the issues raised in Part 1 including:</p>
<ul>
<li>bugfixes for the inline editor</li>
<li>auto toggling of editing state using CSS content generation and the angular $scope</li>
<li>leveraging the DSL from the first screencast as an interface to a 3rd party JavaScript data grid component: js-grid</li>
</ul>
<p>If you’re interested in some more context prior to watching, check out my other <a href="https://www.youtube.com/c/DavidMosher">angular screencasts</a> and an earlier post on the <a href="https://blog.davemo.com/posts/2013-06-26-what-polymer-and-angular-tell-us-about-the-future-success-of-the-web-platform-and-javascript-frameworks">power of web components as abstractions</a>.</p>
<p>Hopefully the next screencast series won’t take 2 years to complete ;)</p>
<h2 id="code">Code</h2>
<ul>
<li>bugfix: don’t re-add and compile editors <a href="https://github.com/davemo/advanced-directives-with-angular-js/commit/4efc9edfacc3cee791f155d52bf517a7ab251586">https://github.com/davemo/advanced-directives-with-angular-js/commit/4efc9edfacc3cee791f155d52bf517a7ab251586</a></li>
<li>feature: swap arrow on editor state <a href="https://github.com/davemo/advanced-directives-with-angular-js/commit/2f046f51dda4b54891353b7ec047b3a6e381792d">https://github.com/davemo/advanced-directives-with-angular-js/commit/2f046f51dda4b54891353b7ec047b3a6e381792d</a></li>
<li>feature: leverage a 3rd party lib using the same DSL <a href="https://github.com/davemo/advanced-directives-with-angular-js/pull/2/files">https://github.com/davemo/advanced-directives-with-angular-js/pull/2/files</a></li>
</ul>
<h2 id="resources">Resources</h2>
<p>This is part of a screencast series on Angular JS</p>
<ol>
<li><a href="http://www.youtube.com/watch?v=8ILQOFAgaXE">Intro to Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=hqAyiqUs93c">End to End with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=18ifoT-Id54">Security with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=fSAgFxjFSqY">Frontend Workflows with Grunt and Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=UYVcY9EJcRs">Testing Strategies for Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=Ty8XcASK9js">Advanced Directives with Angular JS (Part 1)</a></li>
<li><a href="https://www.youtube.com/watch?v=4zG8SfucUzg">Advanced Directives with Angular JS (Part 2)</a></li>
</ol>
]]></description><link>https://blog.davemo.com/posts/2017-03-07-advanced-directives-with-angular-js-part-2.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2017-03-07-advanced-directives-with-angular-js-part-2.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Tue, 07 Mar 2017 00:00:00 GMT</pubDate></item><item><title><![CDATA[Reasons not to quit your job]]></title><description><![CDATA[<aside class="tldr">
Think hard before you quit your job. Consider whether you might be quitting to chase technology, scale, or to run from adversity.
</aside>

<p>2016 was a year of ups and (if you agree with many of the social media channels in the last month) a significantly larger number of downs. I’m not sure  <em>exactly</em> what it is about this time of year that causes us to reflect so much more than the rest of the year; I suspect it <em>mostly</em> has to do with a heightened sense of awareness as we approach boundaries.</p>
<p>As children, we know they are there because our parents tell us and we frequently come close to them often by virtue of desiring to push past to see what is beyond. As parents, we know they are there because we create them; often intending to protect our children from things that are dangerous or to set parameters for healthy  age-appropriate interaction. As software developers we encounter <a href="https://www.destroyallsoftware.com/talks/boundaries">boundaries</a> in tests, the design of our code as it relates to components in a system, API’s, the list goes on and on. Being in constant consideration of boundaries, whether consciously or subconsciously, has the side-effect of switching our brain into analysis and reflection mode.</p>
<p>I recently had some friends reach out for advice as they were contemplating a transition at a critical boundary: quitting their job.</p>
<blockquote>
<p>It’s time for a change</p>
</blockquote>
<blockquote>
<p>I’ve been restless but I’m trying to be cautious</p>
</blockquote>
<p>These exchanges, coupled with the time of year and my brain switching into reflection mode, have had me considering reasons I left jobs in the past. The more I thought about it the more my brain kept telling me that those reasons were often <em>not</em> valid reasons to justify leaving. I wrestled with it and tried to move onto other thoughts but it just kept nagging at me; this is usually an indication I need to deal with those thoughts in a positive way. So, here I am attempting to impart some wisdom about reasons <em>not</em> to quit your job in 2017 (but mostly just typing as an exercise to free my brain up to move onto other things!) 😉</p>
<h2 id="technology">Technology</h2>
<p>On April 3rd, 2014 I wrote <a href="https://blog.davemo.com/posts/2014-04-03-the-magnetic-core-philosophy.html">some of the reasons</a> I left the last job I was at. I’ve re-read that post a dozen times trying to mine some fragments of wisdom, mostly to attempt to justify the decisions at that time. The only conclusions I’ve come up with at this point are: there are a lot of emotions in my writing that stemmed <em>mainly</em> from too much focus on Technology as one of the reasons I quit.</p>
<p>Technology is seductive; it attempts to woo us through flashy demos from paid evangelists or conference talks from influential heroes. Technology is like the trench-coat wearing crook on the street corner who promises us the Rolex and ends up delivering a cheap imitation. When the tech-stack you work with (or <em>want</em> to work with) becomes the justification for a career change it has the potential to snowball into a full on addiction to newness that sets your brain up for a dependence on switching tech in the same way a drug addict depends on their narcotic of choice.</p>
<p>The truth is that most systems that we work on move slowly towards <a href="https://en.wikipedia.org/wiki/Software_entropy">disorder and complexity</a>, so it is no surprise that the promise of a fresh-start in a new technology stack is appealing. An entire generation of software developers have become so accustomed to quitting their job based on technology that I suspect the coming decades will yield a significant need for people who have experience dealing with systems over a long period of time. If we don’t deal with our addiction to newness, the recruiter pitch of “Junior Developer Wanted, 5 years experience Node.JS/Rails/…” that we so frequently deride will turn into “Senior Developer Wanted, 10 years experience dealing with software entropy” and there will be no one to answer the call.</p>
<p>Don’t get sucked into the new tech cycle; learn to embrace what you’re working on now and strive to work the best with the technology constraints you’ve been handed.</p>
<h2 id="scale">Scale</h2>
<p>So, you’ve thought considerably about your current job and ruled out technology as the main reason for leaving. In fact, you’re sticking with the tried and true tech-stack you’ve always known, but there’s just been that nagging feeling that you’d be so much better off if you could experience working with your stack at a larger scale. Oh to be able to understand the constraints of systems at an order-of-magnitude or two beyond where you are working now; that’s the ticket!</p>
<p>Scale is subtle; on the surface it promises a similar thing to the tech-stack switch: an opportunity to learn new things that your current scale of operations can’t teach you. Chances are you will probably actually learn some new technical things by increasing scale; the reality is that we tend to focus more on the <em>technical</em> and less on the <em>human</em> side of software development which often affects increases in scale much more. In his book, <a href="https://www.predictablesuccess.com/books/predictable-success/">Predictable Success</a>, Les McKeown gives some wisdom about how scale affects success in an operation:</p>
<blockquote>
<p>Predictable Success is defined as one of the seven stages of organizational development. It can be attained by any organization or group that acts as a human machine, well-oiled, working in concert, not without challenge, but focused and “in the zone” where growth is attainable <em>and</em> sustainable. It is not about size or the age of an organization. It has nothing to do with resources, culture or industry.</p>
</blockquote>
<p><img src="/img/reasons-not-to-quit-your-job/predictable.success.whole.picture.png" alt="The Whole Picture of Organizational Development according to Predicable Success"></p>
<p>I particularly like his focus on the “human” factors and exclusion of the traditionally technical things we tend to focus on. While the book is targeted towards business growth and the reasons why companies tend to fail, it can be broadly applied to <em>any</em> group or organization (including software development orgs). Ask the following questions about the organization you’re contemplating joining:</p>
<ul>
<li>What <a href="/img/reasons-not-to-quit-your-job/predictable.success.whole.picture.png">stage of organizational development</a> are they in?</li>
<li>Can I really grow the way I want to there, not just technically but also in my capacity as a human?</li>
<li>Will the scale inhibit or promote my growth?</li>
<li>What potential problems will exist at the new operating scale?</li>
</ul>
<p>In my experience working as a consultant the only constant I’ve observed is that the same problems (and the same trend towards entropy) exist regardless of the scale of the company and how they use their technology.</p>
<p>Think hard about using scale as justification for quitting your job; chances are you haven’t considered the <em>human</em> factors involved.</p>
<h2 id="adversity">Adversity</h2>
<p>Ruling out both scale and technology as reasons for quitting a job is great, but to be honest they aren’t the most common reasons I’ve quit in the past. Typically that honor is reserved for a more sinister and damaging character-trait: an inability to handle adversity.</p>
<p>Adversity is double-edged; it shows us the best <em>and</em> worst of ourselves, sometimes at the same time! If I take a step back and examine times when I left jobs because things just got too challenging I truly feel like there was <em>always</em> an opportunity to overcome <em>in spite of</em> the challenging situation and emerge a stronger human. How we respond to challenges in our workplace says a lot about our character:</p>
<p><img src="/img/reasons-not-to-quit-your-job/calvin.character.building.png" alt="Calvin Builds Character Shoveling Snow"></p>
<p><img src="/img/reasons-not-to-quit-your-job/calvin.character.building.2.gif" alt="Calvin Builds Character Eating Dinner"></p>
<p><img src="/img/reasons-not-to-quit-your-job/calvin.character.building.3.gif" alt="Calvin Builds Character Being Cold (and Frugal)"></p>
<p>Before you quit your job because it’s just “too damned hard” I would encourage you to take a moment and step outside yourself; reflect on the situation and the facts – <a href="https://www.vitalsmarts.com/crucialskills/2009/04/how-to-control-your-emotion/">take the emotion out of it</a> and be objective.</p>
<p>If you can turn a challenging situation from a painful pity-party into an opportunity to conquer and emerge better you will be amazed at what you can come through.</p>
<h2 id="still-ready-to-quit">Still Ready to Quit?</h2>
<p>There are valid reasons to quit your job, but I think we tend to dwell on them far too often before considering the reasons <em>not</em> to. My hope for you at the end of 2016 is that as you approach the boundary and transition leading into 2017 that you would be prompted to introspect and reflect on what it is you truly want. Set some goals, list some objectives for your life and above all ask yourself some tough questions before throwing in the towel; as a wise friend of mine once said:</p>
<blockquote>
<p>If the grass is greener on the other side, then you’d better water your own lawn.</p>
</blockquote>
]]></description><link>https://blog.davemo.com/posts/2016-12-30-reasons-not-to-quit-your-job.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2016-12-30-reasons-not-to-quit-your-job.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 30 Dec 2016 00:00:00 GMT</pubDate></item><item><title><![CDATA[Introduction to React Native]]></title><description><![CDATA[<aside class="tldr">
A basic introduction to getting started with React Native.
</aside>

<p>This screencast shows how to get setup, development workflow, and building the first cut at a ListView to show some images and meta data for <a href="https://playhearthstone.com/en-us">Hearthstone</a> cards in the React Native application workflow.</p>
<iframe src="https://www.youtube.com/embed/n5RhAYhTxCk?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<h2 id="resources">Resources</h2>
<p><a href="https://facebook.github.io/react-native/">https://facebook.github.io/react-native/</a></p>
]]></description><link>https://blog.davemo.com/posts/2015-04-10-introduction-to-react-native.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2015-04-10-introduction-to-react-native.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 10 Apr 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[Building DSLs with JavaScript & CoffeeScript]]></title><description><![CDATA[<aside class="tldr">
Creating DSLs (domain-specific languages) can be a path to useful abstraction, but it can also lead to complexity.
</aside>

<p>Web developers are integration specialists—tying plugins, scripts and frameworks together into a web application that works. Thinking in terms of abstractions—by condensing many low-level ideas into fewer high-level ideas—allows us to simplify our code and reason about it with less cognitive overhead.</p>
<iframe src="https://www.youtube.com/embed/EOksrrySfwI?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<p>In this screencast, recorded at <a href="prairiedevcon.com">Prairie Dev Con 2015</a>, we examine a few techniques for building abstractions on top of popular JavaScript frameworks by learning about Domain Specific Languages and bringing some convention to our code.</p>
<h2 id="code">Code</h2>
<p><a href="https://github.com/davemo/jsdsl">https://github.com/davemo/jsdsl</a></p>
]]></description><link>https://blog.davemo.com/posts/2015-03-06-building-dsls-with-javascript-and-coffeescript.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2015-03-06-building-dsls-with-javascript-and-coffeescript.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 06 Mar 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[Advanced Directives with AngularJS (Part 1)]]></title><description><![CDATA[<aside class="tldr">
AngularJS treats HTML as a DSL, allowing us to create abstractions using markup through its concept of directives.
</aside>

<p>This screencast examines some of the more advanced features in Angular, specifically Directives. We’ll see how we can leverage the power of custom elements and attributes to map Domain Specific concepts through HTML and translate those into Value Objects in our Domain, resulting in cleaner HTML output. Also discussed: complexity, creating a DSL with directives, debugging techniques, and other tips and tricks.</p>
<p>If you’re interested in some more context prior to watching, check out my other <a href="https://www.youtube.com/user/vidjadavemo/videos">angular screencasts</a> and an earlier post on the <a href="https://blog.davemo.com/posts/2013-06-26-what-polymer-and-angular-tell-us-about-the-future-success-of-the-web-platform-and-javascript-frameworks">power of web components as abstractions</a>. This screencast covers:</p>
<ul>
<li>html as a dsl</li>
<li>abstractions in html</li>
<li><a href="https://docs.angularjs.org/api/ng/service/$compile">$compile</a></li>
<li><a href="https://docs.angularjs.org/api/ng/service/$templateRequest">$templateRequest</a></li>
<li><a href="https://docs.angularjs.org/api/ng/service/$templateCache">$templateCache</a></li>
<li><a href="https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object">directive definition object</a></li>
<li><a href="https://docs.angularjs.org/api/ng/service/$compile#-require-">requiring other directives</a></li>
<li><a href="https://docs.angularjs.org/guide/scope#scope-events-propagation">directive communication ($scope.$broadcast, $scope.$on)</a></li>
</ul>
<iframe src="https://www.youtube.com/embed/Ty8XcASK9js?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>

<h2 id="code">Code</h2>
<ul>
<li><a href="https://github.com/davemo/advanced-directives-with-angular-js">https://github.com/davemo/advanced-directives-with-angular-js</a></li>
</ul>
<h2 id="extra-credit">Extra Credit</h2>
<p>Some things in the screencast aren’t complete and some things could definitely done better. This section is a challenge to you, the reader/watcher to improve the code and level up your knowledge in the process! Try and tackle some of these challenges if you want:</p>
<ul>
<li>Bugfix: the editor currently shows up multiple times, fix it so this doesn’t happen (hint: maybe an ‘edit’ state that’s tracked could help the directive know if it should execute <code>.insertAfter</code>)</li>
<li>Feature: make the expandy arrow thing point down when expanded and to the right when collapsed.</li>
</ul>
<h2 id="resources">Resources</h2>
<p>This is part of a screencast series on Angular JS</p>
<ol>
<li><a href="http://www.youtube.com/watch?v=8ILQOFAgaXE">Intro to Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=hqAyiqUs93c">End to End with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=18ifoT-Id54">Security with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=fSAgFxjFSqY">Frontend Workflows with Grunt and Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=UYVcY9EJcRs">Testing Strategies for Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=Ty8XcASK9js">Advanced Directives with Angular JS (Part 1)</a></li>
<li><a href="https://www.youtube.com/watch?v=4zG8SfucUzg">Advanced Directives with Angular JS (Part 2)</a></li>
</ol>
]]></description><link>https://blog.davemo.com/posts/2015-02-13-advanced-directives-with-angular-js.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2015-02-13-advanced-directives-with-angular-js.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Fri, 13 Feb 2015 00:00:00 GMT</pubDate></item><item><title><![CDATA[The Magnetic Core Philosophy]]></title><description><![CDATA[<aside class="tldr">
When we prioritize technical depth over empathy, we run the risk of over-indexing on coding skills when valuing software developers.
</aside>

<p>Do software companies succeed because of the <strong>technology</strong> they use or is it due to the <strong>discipline and talent</strong> of their engineering and design teams? Is it the right combination of <strong>vision and direction</strong> from leadership that leads to success or just simply being in the right place at the right time? All of these things are potential contributing factors to the success of a software company but none of them stand alone as reasons for success.</p>
<p>The companies that succeed are the ones who solve their customers business problems with excellence, regardless of technology, leadership, or team composition. To attribute success to any one dimension, such as technology choice, is at best naive and at worst renders that dimension untouchable.</p>
<h2 id="alignment-challenges">Alignment Challenges</h2>
<p>When technology is given all of the credit for success it becomes the gravitational center that all further technology decisions <em>must</em> orbit. Evaluating alternatives becomes fruitless; anything that has the potential to drift too far from the ideals of this magnetic core is viewed with skepticism. This makes it challenging for companies to adapt, or even recognize, that there are changes in technology choice that could benefit them greatly.</p>
<p>Worse still, this magnetic core philosophy creates an environment where <em>all</em> choices are evaluated through the reality-warping magnetic field surrounding the technology core. Process changes, HR policies, culture; all these things are influenced by the gravitational pull of the core and often by the community of <em>other</em> companies who have chosen to embrace its ideals.</p>
<h2 id="untouchable-icons">Untouchable Icons</h2>
<p>In a company where the magnetic core of technology is viewed as the sole basis for success, there become untouchable icons (the technology, and its supporters) who are unable to be challenged. This often creates a sense of fear (and paralyzes objective thinking) as any idea that deviates from core ideals is viewed as having the potential to veer the company out of orbit and cause a cascade of failure.</p>
<p>There becomes an identity mismatch between team members who have good ideas and those who espouse core ideals; the framework of trust used to identify an individuals success often selects the latter.</p>
<h2 id="technical-depth-at-the-expense-of-empathy">Technical Depth at the expense of Empathy</h2>
<p>Companies that align their success with the magnetic core of technology often over-value technical depth and promote from within those individuals who have demonstrated alignment with core ideals. When technical depth is put in this position of importance it creates an environment where empathy is seen as a less valuable skillset. The challenges that these companies face, and the challenges our industry continues to face are often a direct result of a commonly repeated failure that too closely aligns success with technology.</p>
<h2 id="moving-forward">Moving Forward</h2>
<p>All of this paints a pretty bleak picture about the state of technology and the software industry and it is easy to list problems without solutions; where do we go from here? I firmly believe that there are simple solutions to the problems listed above.</p>
<p>First, we need to balance the success equation in our companies so that it places more emphasis on <em>people</em> being the reason for success instead of technology choice.</p>
<p>Second, We need to foster an open environment that allows people to feel like they can contribute ideas, any ideas, without feeling paralyzed by the fear of being viewed as counter-culture when ideas differ from core ideals.</p>
<p>Lastly, and most importantly, we need to balance the framework we use to promote leaders in our companies so that empathy will be considered more important than technical depth.</p>
]]></description><link>https://blog.davemo.com/posts/2014-04-03-the-magnetic-core-philosophy.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2014-04-03-the-magnetic-core-philosophy.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Thu, 03 Apr 2014 00:00:00 GMT</pubDate></item><item><title><![CDATA[Testing Strategies for Angular JS]]></title><description><![CDATA[<aside class="tldr">
Choosing a testing strategy has more to do with selecting the right level of fidelity than selecting the right testing framework.
</aside>

<p>This screencast examines low and high fidelity testing strategies for Angular JS, and demonstrates examples of how to write tests with these strategies using Protractor, Testem, and Jasmine.</p>
<iframe src="https://www.youtube.com/embed/UYVcY9EJcRs?wmode=transparent" allowfullscreen frameborder="0" height="417" width="515"></iframe>


<h2 id="code">Code</h2>
<ul>
<li><a href="https://github.com/davemo/lineman-angular-template">https://github.com/davemo/lineman-angular-template</a></li>
</ul>
<h2 id="resources">Resources</h2>
<p>This is part of a screencast series on Angular JS</p>
<ol>
<li><a href="http://www.youtube.com/watch?v=8ILQOFAgaXE">Intro to Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=hqAyiqUs93c">End to End with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=18ifoT-Id54">Security with Angular JS</a></li>
<li><a href="http://www.youtube.com/watch?v=fSAgFxjFSqY">Frontend Workflows with Grunt and Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=UYVcY9EJcRs">Testing Strategies for Angular JS</a></li>
<li><a href="https://www.youtube.com/watch?v=Ty8XcASK9js">Advanced Directives with Angular JS (Part 1)</a></li>
<li><a href="https://www.youtube.com/watch?v=4zG8SfucUzg">Advanced Directives with Angular JS (Part 2)</a></li>
</ol>
]]></description><link>https://blog.davemo.com/posts/2013-08-08-testing-strategies-for-angular-js.html</link><guid isPermaLink="true">https://blog.davemo.com/posts/2013-08-08-testing-strategies-for-angular-js.html</guid><dc:creator><![CDATA[David Mosher]]></dc:creator><pubDate>Thu, 08 Aug 2013 00:00:00 GMT</pubDate></item></channel></rss>