<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://jordanforeman.com/rss.xml" rel="self" type="application/atom+xml" /><link href="https://jordanforeman.com/" rel="alternate" type="text/html" /><updated>2026-08-12T21:34:56+00:00</updated><id>https://jordanforeman.com/rss.xml</id><title type="html">Jordan Foreman</title><subtitle>👋 I&apos;m Jordan Foreman!</subtitle><author><name>Jordan Foreman</name><email>hello@jordanforeman.com</email></author><entry><title type="html">Be a (Good) Meat Proxy</title><link href="https://jordanforeman.com/2026/be-a-good-meat-proxy/" rel="alternate" type="text/html" title="Be a (Good) Meat Proxy" /><published>2026-08-12T00:00:00+00:00</published><updated>2026-08-12T00:00:00+00:00</updated><id>https://jordanforeman.com/2026/be-a-good-meat-proxy</id><content type="html" xml:base="https://jordanforeman.com/2026/be-a-good-meat-proxy/"><![CDATA[<p>In his recent post, <a href="https://gruhn.me/blog/2026-08-03/">Don’t be a Meat Proxy</a>, Niklas Gruhn urges readers not to be a “Meat Proxy”—that is, someone who simply forwards or regurgitates AI output without providing any meaningful value. This framing has clearly resonated with folks. While I take Gruhn’s point, I think the framing - and especially the public response to it - flattens a more nuanced issue.</p>
<h2 id="you-already-are-a-meat-proxy">You Already Are a “Meat Proxy”</h2>

<p>Once an engineer attains some semblance of seniority, the job requirements quickly evolve past simply authoring code. Reviewing designs, managing stakeholders, collecting requirements—these are the things that take most senior engineers’ time. They are also, effectively, examples of being a “meat proxy”.</p>

<p>Consider the last time you were tasked with building something, and realized halfway through that there’s an edge case where the requirements were unclear. Pretty common, no? So you find the product owner, ask a few questions, and return to the work with a clearer sense of the problem. In the abstract: you simply took context from one individual and applied it to a different operation. Meat proxy. What makes this example notable, however, is the human judgment and communication required for it to be done effectively.</p>
<h2 id="but-youre-a-smart-meat-proxy">But You’re a “Smart” Meat Proxy</h2>

<p>The term “meat proxy” is funny, which is certainly one of the reasons it caught on so quickly. But it’s also evocative of a real phenomenon. People do actually just copy and paste AI output trying (unsuccessfully in most cases) to pass it off as their own. This is <em>one</em> type of proxying behavior, I suppose. But consider the fact that there are different kinds of proxies, and you can be any kind of meat proxy you wanna be, baby:</p>
<h3 id="low-latency-dumb-proxies">Low-Latency “Dumb” Proxies</h3>

<p>This is the type of proxy that Gruhn is getting at. This type of proxy simply takes its input and forwards it along. Nothing too interesting here, and yes—don’t be <em>this</em> kind of meat proxy.</p>
<h3 id="transforming-proxies">Transforming Proxies</h3>

<p>Some proxies take their input and modify it in some way before passing it along to the destination. This is what Gruhn advocates for:</p>

<blockquote>
  <p>Don’t just relay the output. Read it, understand it, validate it, and then write a response in your own words (a decent certificate that you’ve done the prior steps). Making that effort is value you can add.</p>
</blockquote>

<p>Knowing <em>when</em> and <em>how</em> to do this, however, is crucial. Reframing <em>every</em> output an AI makes just to prove that you’ve done the cognitive work is wasted effort. Knowing which outputs to transform (e.g., research) and which to pass along verbatim (e.g., code) is a skill in itself. Transforming that information in a legible way for the recipient takes communication skills, and has always been what separates the best engineers from the only-okay.</p>
<h3 id="routing-proxies">Routing Proxies</h3>

<p>Now this is an interesting example. Traditionally proxies have had the responsibility of routing (e.g., service discovery in distributed systems, load balancing, etc.). AI is an excellent thinking partner, but can often act like a firehose of information. When a dumb proxy simply regurgitates all of this prose, it’s both obvious and harmful. When a smart “routing” proxy dissects this output and routes the correct information to its destination (e.g., incorporating new constraints, elevating new questions), the value of the AI thinking partner can be more fully realized.</p>
<h2 id="be-a-good-human">Be a Good Human</h2>

<p>Knowing “when” and “how” to shape AI output and how to correctly route that output are the key skills one needs to be an effective proxy. Remember that your audience isn’t (or shouldn’t be) just another AI—it’s a human. Code changes, technical designs, product briefs, emails, <strong>blog posts</strong>—these are artifacts that are meant to be read by <em>a human</em>. They can inform future AI as context, but they are first and foremost means of communicating with people. You should be shaping the output for the audience.</p>

<p>The answer isn’t to avoid being a proxy altogether; it’s to be an intelligent one whose judgment shapes the output to be more useful to the human on the receiving end.</p>]]></content><author><name>Your Name</name></author><category term="AI" /><summary type="html"><![CDATA[In his recent post, Don’t be a Meat Proxy, Niklas Gruhn urges readers not to be a “Meat Proxy”—that is, someone who simply forwards or regurgitates AI output without providing any meaningful value. This framing has clearly resonated with folks. While I take Gruhn’s point, I think the framing - and especially the public response to it - flattens a more nuanced issue. You Already Are a “Meat Proxy”]]></summary></entry><entry><title type="html">The Shape of Things to Come</title><link href="https://jordanforeman.com/2026/the-shape-of-things-to-come/" rel="alternate" type="text/html" title="The Shape of Things to Come" /><published>2026-08-11T00:00:00+00:00</published><updated>2026-08-11T00:00:00+00:00</updated><id>https://jordanforeman.com/2026/the-shape-of-things-to-come</id><content type="html" xml:base="https://jordanforeman.com/2026/the-shape-of-things-to-come/"><![CDATA[<p>I recently bought my family our first 3D printer and I am blown away by the technology.</p>

<p>I know that 3D printing isn’t a new technology - it’s been around for decades, and consumer-grade 3D printers have been available on the market for several years at this point. I’m actually <em>late</em> to the game here.</p>

<p>But I can’t help but be amazed at this piece of technology. It’s a frickin’ real-life <a href="https://en.wikipedia.org/wiki/Replicator_(Star_Trek)">replicator</a>!</p>

<p>Of course, after printing my first few doodads and dongles from Bambu’s extensive <a href="https://makerworld.com/">MakerWorld</a> catalog, my mind immediately pivoted to another modern consumer technology that I am certainly not a late adopter of: AI.</p>

<p>There’s no reason I couldn’t create my own designs. If I can imagine it, why shouldn’t I be able to print it? Except that I have zero CAD experience. But it’s 2026! Any CAD software is just one representation of bytes on a computer - bytes that a properly instructed AI agent can tweak and tune at <em>my</em> discretion.</p>

<p>After a few short hours of playing around, I’ve got a rough workflow: I chat with <a href="https://pi.dev/">Pi</a>, which <strong>programmatically</strong> uses <a href="https://openscad.org/">OpenSCAD</a> or <a href="https://blender.org/">Blender</a> to create actual three-dimensional designs, then exports them to <a href="https://bambulab.com/en/download/studio">Bambu Studio</a> as 3MF files. From there, I can send them to my replicator and hold the results in my hands within hours.</p>

<p>Again, <strong>mind-blowing</strong>.</p>

<p>The fact that it was so trivial for me to compose these various tools and turn nearly anything I can dream up into reality is nothing short of miraculous. It’s simply incredible how accessible all of this stuff really is. The dream-to-reality pipeline is getting shorter by the day. To quote the Zuck: <a href="https://about.fb.com/news/2026/08/the-future-is-for-everyone/">The Future Is for Everyone</a>.</p>]]></content><author><name>Your Name</name></author><category term="Futurism" /><summary type="html"><![CDATA[I recently bought my family our first 3D printer and I am blown away by the technology.]]></summary></entry><entry><title type="html">Learning to Read (Code)</title><link href="https://jordanforeman.com/2026/learning-to-read-code/" rel="alternate" type="text/html" title="Learning to Read (Code)" /><published>2026-02-09T00:00:00+00:00</published><updated>2026-02-09T00:00:00+00:00</updated><id>https://jordanforeman.com/2026/learning-to-read-code</id><content type="html" xml:base="https://jordanforeman.com/2026/learning-to-read-code/"><![CDATA[<blockquote>
  <p>Programs must be written for people to read, and only incidentally for machines to execute</p>
</blockquote>

<p><a href="https://www.goodreads.com/quotes/9168-programs-must-be-written-for-people-to-read-and-only">Structure and Interpretation of Computer Programs (1984)</a></p>

<hr />

<p>Reviewing code is a core responsibility of software engineers — but we do not teach it. We spend much of our academic years learning to <em>write</em> code, our formative years sharpening that skill, and our latter years debating tabs vs spaces. Yet very little effort is put into teaching how to <em>read</em> code. We assume that if you can write code, surely you can read it! But “reading” and “writing”, while related, are two totally different skills (just ask my five year old - he’s learning to do both!)</p>

<p>With the continued advances in agentic development, authoring code is no longer the bottleneck. What used to take hours or even days can be accomplished in mere minutes with the help of LLMs. This has unleashed a torrent of creativity, new features, and much more code that needs to be carefully inspected. <strong>Reading</strong> code is a critical skill that must be deliberately sharpened.</p>

<p>So how do you learn? Let me tell you how I did.</p>

<h3 id="starting-from-zero">Starting from Zero</h3>

<p>While I’ve been responsible for doing code reviews my entire career, it not something that I focused on until I had been a senior engineer for some time. I spent my early years bouncing around a few different industries and earning my stripes. After that I spent a year as the lead (see: only) engineer of a small local startup. Given that I worked alone I received zero feedback and since I was the only one writing any code I had zero feedback to give. Code reviews were nonexistent. Once that startup failed I found myself thrust into the exact opposite situation: working in a massive organization with seemingly-insurmountable complexity and more engineers with opinions than you could shake a stick at!</p>

<p>In order to compensate, I started asking questions. My aptitude was in code, and since that’s where I was most comfortable, that’s where I directed my efforts:</p>

<ul>
  <li>What does this language syntax mean?</li>
  <li>Where can I find this method?</li>
  <li>What if you wrote it this way?</li>
</ul>

<p>These questions, while basic and sometimes even pedantic, helped me orient myself in this chaotic new environment. Through asking these simple questions I was able to form relationships with my new peers, immerse myself in the language and tools that we used, and (perhaps most importantly) establish the confidence to keep asking questions as those questions started to become more informed.</p>

<p>Note that every example above was a question, not a statement. Its okay not to have the answers. Its <em>expected</em>! There truly is no such thing as a dumb question. <em>Any</em> question that you ask will result in one of two things happening (maybe even both!): the author reconsiders their code from a new perspective, or you learn something new that you can take with you.</p>

<p>Just because its easier to focus on the technology (its what we’re experts in after all), that doesn’t diminish the value in doing so. I started my code review journey focusing on those things - but they’re still far and away the majority of review comments I leave. It’s fun to quibble with and push for cleaner, more well-designed code! It’s important too!</p>

<h3 id="growing-the-context-window">Growing the Context Window</h3>

<p>After a few months into my new position I had developed a much stronger understanding of the business and the players involved. With this growth in context came bigger questions:</p>

<ul>
  <li>Will this change help or hinder next week’s planned feature work?</li>
  <li>Should we really couple this implementation to <em>that</em> tool when we’re looking at migrating to <em>this other tool</em>?</li>
</ul>

<p>These are the sorts of questions that arise when you consider a changeset as just one component of delivering customer value. A PR represents, in the micro, a gradual step - but a step towards <em>what</em>? Having a concrete understanding of the team’s goals and ever-evolving philosophy should inform how we consider individual code changes.</p>

<p>This is the part where <em>writing</em> code and <em>reading</em> code really diverge as skills. The misconception that “software engineering” equals “writing code” is understandable — after all, it’s the most tangible thing we do. But think about the last time you built something consequential. You researched the problem, talked to the people who knew more, formulated a plan, <em>then</em> wrote the code, <em>then</em> had several people review it. The actual writing was already just a small part. It is the complex cerebral work required to write <em>the correct</em> code and get it into the hands of customers that is the proper role of engineering — and reviewing code is where that broader context pays dividends.</p>

<h3 id="thinking-across-boundaries">Thinking Across Boundaries</h3>

<p>Fast-foward to a couple of years later. I’m working as the tech lead on a different team, and the context I brought to my reviews had grown further still:</p>

<ul>
  <li>Is this change going to conflict with what <em>that other team</em> is building right now? Or plans to build next month?</li>
  <li>This change couples us to a conception of the domain model that is highly volatile. Can we reconsider this change or wait until we have clearer architectural guidance?</li>
</ul>

<p>Same habit, bigger aperture. I was still “just asking questions” — but the questions now drew on context that spanned teams, timelines, and architecture. You get here the same way you got to the previous stage: by expanding your awareness. Meet new people for coffee chats to discuss their work. Review incoming requests from outside contributors. The more you understand about <em>why</em> things are being built, the more useful your reviews become.</p>

<h3 id="now-its-your-turn">Now its Your Turn</h3>

<p>Observing my own personal journey reveals two key themes:</p>

<ol>
  <li><strong>Always Ask Questions.</strong> Start where you’re comfortable — syntax, style, whatever. The questions get bigger as your context grows, but the habit is the same from day one.</li>
  <li><strong>Always Be Expanding Your Context Window.</strong> Following directly from asking questions, you should always be increasing your awareness (not necessarily concrete knowledge) of the product/team/organizational context within which you’re reviewing code.</li>
</ol>

<p>It takes humility and bravery to ask questions and engage in conversations. Start asking them anyway.</p>]]></content><author><name>Your Name</name></author><category term="Code Review" /><summary type="html"><![CDATA[Programs must be written for people to read, and only incidentally for machines to execute]]></summary></entry><entry><title type="html">The Point of Coverage</title><link href="https://jordanforeman.com/2025/code-coverage/" rel="alternate" type="text/html" title="The Point of Coverage" /><published>2025-12-18T00:00:00+00:00</published><updated>2025-12-18T00:00:00+00:00</updated><id>https://jordanforeman.com/2025/code-coverage</id><content type="html" xml:base="https://jordanforeman.com/2025/code-coverage/"><![CDATA[<p>I’ve long said that 100% test coverage should be table stakes for any production system. Yes, I know all the objections - “coverage for coverage’s sake,” “you get what you measure,” “it measures the wrong thing.” I agree with every single criticism. Yet I still insist on 100%.</p>

<p>Test coverage has long been a controversial topic. Management often equates coverage percentage to code quality, pushing for teams to meet some arbitrary threshold that will magically ensure that we’re writing high-quality code. While I agree with the criticisms, I still insist that 100% coverage <strong>must</strong> be our baseline for any production system.</p>
<h3 id="-100-coverage-what-is-it-good-for">🍀 100% Coverage; What is it Good For?</h3>

<p>While the clichéd retorts against coverage metrics are not without merit, they are only directionally correct. Insisting on 100% coverage for its own sake is a fool’s errand. Total coverage is still a worthy goal as a means of developing quality software. While coverage doesn’t indicate quality the deliberate pursuit of coverage enables quality in unique and powerful ways.</p>

<p>By striving to test each and every function, branch, and statement we write we force ourselves to engage with our code’s rough edges in the most intimate ways as early as possible. The tests we write are often the first time our code is engaged with - but it will not be the last! Testing puts the evolutionary pressure of “testability” on our design. By listening closely to what that pressure tells us we can immediately spot issues with the long-term maintainability of our code before anyone else even sees it.</p>

<p>If we approach testing our code with this mindset, 100% code coverage is simply an artifact of a job well done.</p>
<h3 id="-the-hidden-curriculum-of-test-coverage">🎓 The Hidden Curriculum of Test Coverage</h3>

<p>Most professional engineers that I’ve had the pleasure to work with generally do attempt to thoroughly test the code that they write. They identify the “happy path” but also ensure that a few obvious edge cases are covered as well. If they can achieve 100% coverage in this way, all the better - but so long as they’re not dipping below any automated baselines (eg. failing continuous integration builds) then this is an optional cherry on top of their work.</p>

<p>Those programmers who do set their sights higher often buckle at the first sign of adversity. Can’t come up with a clear API invocation that covers that one branch in that private method you wrote? “Eh, its probably not <em>critical</em> to test that branch. We do need to have it though just in case X, Y, or Z reason”.  This self-delusion is a trap - any production code that warrants being written definitionally deserves to be tested. If we anticipate this pattern, however, we can identify it for what it really is: a signal about the quality of our source code.</p>
<h3 id="-when-testing-gets-hard-listen">🙉 When Testing Gets Hard, Listen</h3>

<p>There are often scenarios where contriving a test scenario that mimics <em>exactly</em> what some edge case is programmed for is simply too difficult or even impossible. It is these exact scenarios that inform us that our design needs refactoring.</p>

<p>Private methods are by definition implementation details and as such should not be tested explicitly. That implementation detail, however, reflects a real use case of our system. While it is <em>usually</em> trivial to establish a test whose setup puts the system in a state that mirrors this “real-world scenario”. However any engineer who has written tests for any substantial amount of time in their career can tell you that this is not always the case.</p>

<p>Imagine that you’re building a feature that sends notifications to users, but only if they haven’t been notified recently (to avoid spam). This might look something like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">NotificationService</span>
  <span class="k">def</span> <span class="nf">notify_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="n">should_send_notification?</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
      <span class="n">send_email</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">email</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
      <span class="n">send_sms</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">phone</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">premium?</span>
      <span class="n">log_notification</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
  
  <span class="kp">private</span>
  
  <span class="k">def</span> <span class="nf">should_send_notification?</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">notifications_paused?</span>
    
    <span class="n">last_notification</span> <span class="o">=</span> <span class="no">Notification</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">user_id: </span><span class="n">user</span><span class="p">.</span><span class="nf">id</span><span class="p">)</span>
                                   <span class="p">.</span><span class="nf">order</span><span class="p">(</span><span class="ss">created_at: :desc</span><span class="p">)</span>
                                   <span class="p">.</span><span class="nf">first</span>
    
    <span class="k">return</span> <span class="kp">true</span> <span class="k">if</span> <span class="n">last_notification</span><span class="p">.</span><span class="nf">nil?</span>
    
    <span class="n">time_since_last</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="o">-</span> <span class="n">last_notification</span><span class="p">.</span><span class="nf">created_at</span>
    <span class="n">time_since_last</span> <span class="o">&gt;</span> <span class="n">throttle_period</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
  <span class="k">end</span>
  
  <span class="k">def</span> <span class="nf">throttle_period</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">premium?</span> <span class="p">?</span> <span class="mi">1</span><span class="p">.</span><span class="nf">hour</span> <span class="p">:</span> <span class="mi">24</span><span class="p">.</span><span class="nf">hours</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This isn’t a particularly complicated class. It is generally cohesive, as all of this logic lives together. However when we attempt to fully test it, we run into problems quick. For example, how do you test the branch where <code class="language-text highlighter-rouge">last_notification.nil?</code> is <code class="language-text highlighter-rouge">false</code> and time-based logic kicks in?</p>

<p>The private method clearly does too much: database access, time calculations, business rules. Attempting to test this, while not impossible, introduces quite a lot of friction: you need to create database records, manipulate timestamps, and stub <code class="language-text highlighter-rouge">Time.now</code> - to name just a few things.</p>
<h4 id="-code-that-is-easy-to-test-is-easy-to-maintain">🐣 Code that is Easy to Test is Easy to Maintain</h4>

<p>Below is just one example of how we might respond to that difficulty. We can turn the difficult-to-validate <code class="language-text highlighter-rouge">should_send_notification?</code> method into a dedicate class that more clearly models our notification domain:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">NotificationService</span>
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">notification_policy</span><span class="p">:)</span>
    <span class="vi">@notification_policy</span> <span class="o">=</span> <span class="n">notification_policy</span>
  <span class="k">end</span>
  
  <span class="k">def</span> <span class="nf">notify_user</span><span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="vi">@notification_policy</span><span class="p">.</span><span class="nf">allowed?</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
      <span class="n">deliver_notification</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
  
  <span class="kp">private</span>
  
  <span class="k">def</span> <span class="nf">deliver_notification</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="n">send_email</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">email</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
    <span class="n">send_sms</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">phone</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">premium?</span>
    <span class="n">log_notification</span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="nf">id</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">NotificationPolicy</span>
  <span class="k">def</span> <span class="nf">allowed?</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">notifications_paused?</span>
    
    <span class="n">last_sent_at</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">last_notification_sent_at</span>
    <span class="k">return</span> <span class="kp">true</span> <span class="k">if</span> <span class="n">last_sent_at</span><span class="p">.</span><span class="nf">nil?</span>
    
    <span class="n">time_since_last</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="o">-</span> <span class="n">last_sent_at</span>
    <span class="n">time_since_last</span> <span class="o">&gt;</span> <span class="n">throttle_period</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
  <span class="k">end</span>
  
  <span class="kp">private</span>
  
  <span class="k">def</span> <span class="nf">throttle_period</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
    <span class="n">user</span><span class="p">.</span><span class="nf">premium?</span> <span class="p">?</span> <span class="mi">1</span><span class="p">.</span><span class="nf">hour</span> <span class="p">:</span> <span class="mi">24</span><span class="p">.</span><span class="nf">hours</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This approach has several immediate benefits, such as:</p>

<ul>
  <li><code class="language-text highlighter-rouge">NotificationPolicy</code> can be tested in isolation with simple user objects/doubles</li>
  <li>Time-based logic is separated from delivery mechanism</li>
  <li>Adding new notification channels doesn’t touch the policy logic</li>
</ul>

<p>But perhaps more important than these immediate improvements is the reduced costs of maintenance over time. Imagine that six months later, you need to add “quiet hours” (no notifications 10pm-8am). In the original implementation, you’d be digging into that tangled private method. In our refactored version you add one method to <code class="language-text highlighter-rouge">NotificationPolicy</code> and one additional condition in <code class="language-text highlighter-rouge">allowed?</code>. The separation makes the change obvious and safe.</p>

<p>By leaving difficult-to-test code untested, we abandon signal that is crucial to ensuring our code is maximally resistant to entropy. Untested paths are where these design smells hide and you cannot improve that which you cannot smell.</p>

<p>Scaling these marginal improvements in maintainable design across every feature we implement pays dividends that counteract any perceived development costs. Each new feature becomes easier and easier to implement as our systems become more and more flexible.</p>
<h3 id="️-tdd-as-a-real-time-feedback-loop">⚡️ TDD as a Real-Time Feedback Loop</h3>

<p>Anyone who has worked with me for any stretch of time knows that I am a passionate advocate for Test-Driven Development. By flipping the traditional script on its head, we’re able to force the signals we’re concerned with to appear far earlier in the software development lifecycle than ever before!</p>

<p>Think about it: even in the world where you’re writing tests <em>after</em> production code you’re thinking of edge cases and considering their costs and mitigations in the moment. By codifying these considerations in your specifications before even writing the code you put yourself in a position to write the correct APIs and design the right system before a single line of code has even been authored! This is the real power of TDD.</p>

<p>Done correctly, TDD also implies one hundred percent test coverage.</p>
<h3 id="-coverage-is-a-process-not-a-target">🎯 Coverage is a Process, not a Target</h3>

<p>While I strongly believe that TDD is the best way to approach writing our code, it’s not a hard requirement for using code coverage to force better design decisions. What’s important is that we ensure total coverage and use each point of friction we find as a means of identifying improvements to be made.</p>

<p>It’s important to note that simply surfacing <em>opportunities</em> for improvement is not itself a silver bullet. Improved testability does not equate better design; it is simply one dimension of good code. Testable code isn’t necessarily well designed code, but well designed code is inherently testable. While aiming for testability puts pressure on the software’s design, that pressure is not dictatorial. You are still the one making the design decisions and those decisions are where things become problematic.</p>
<h3 id="-brute-forcing-coverage-is-an-anti-pattern">🦍 Brute Forcing Coverage is an Anti-pattern</h3>

<p>Never before has it been more tempting to achieve a higher coverage number through brute force. LLMs and the agents that they power offer the ability to easily define a metric of success (coverage) and the context needed to achieve it. Viewing coverage as a target removes any value from derived from the process.</p>

<p>Without very detailed prompting, an LLM will always simply attempt to write the easiest possible test that moves the coverage metric in the right direction - whether it be abusing mocks and stubs or forcing private method calls. Agent-driven coverage efforts will optimize for the wrong metric to the exclusion of all else.</p>

<p>“No pain, no gain” is an ancient adage for a reason. Offloading the pain of achieving coverage to an LLM that cannot feel the pain and recognize it for what it is abandons any signal and removes the human spark that is needed to take that signal and turn it into a more maintainable design.</p>
<h3 id="️-100-coverage-is-good-actually">🌶️ 100% Coverage is Good, Actually</h3>

<p>It can be easy to fall into the trap of seeing gaps in coverage as a problem to be solved. Viewing coverage gaps in this way can easily lead to missing what those gaps really are - missed signals. By aiming for complete coverage as part of day-to-day engineering work, we shift that crucial signal leftward and amortize the cost of design across the entire SDLC at the time of highest leverage - initial development. If we approach coverage gaps retroactively with this same mentality, we may lose some flexibility, but we can still find useful signal to point ourselves in the direction of a successful refactor.</p>

<p>So the next time someone argues against 100% coverage, ask them this: are you arguing against a metric, or are you arguing against listening to your code?</p>]]></content><author><name>Your Name</name></author><category term="Testing" /><summary type="html"><![CDATA[I’ve long said that 100% test coverage should be table stakes for any production system. Yes, I know all the objections - “coverage for coverage’s sake,” “you get what you measure,” “it measures the wrong thing.” I agree with every single criticism. Yet I still insist on 100%.]]></summary></entry><entry><title type="html">Two Hands on the Wheel</title><link href="https://jordanforeman.com/2025/two-hands-on-the-wheel/" rel="alternate" type="text/html" title="Two Hands on the Wheel" /><published>2025-12-03T00:00:00+00:00</published><updated>2025-12-03T00:00:00+00:00</updated><id>https://jordanforeman.com/2025/two-hands-on-the-wheel</id><content type="html" xml:base="https://jordanforeman.com/2025/two-hands-on-the-wheel/"><![CDATA[<p>There’s this metaphor that’s stuck with me for years: when you’re driving a car, you don’t just aim it in a direction and let it go. Instead, you’re constantly adjusting at the micro level—holding the wheel, turning it a micrometer to the left or to the right to make sure you’re on track.</p>

<p>This metaphor applies to a lot of things, but especially to software engineering. And the most concrete example of this in agile software development is the retrospective.</p>
<h2 id="the-most-undervalued-ritual">The Most Undervalued Ritual</h2>

<p>I’ve long been an advocate for retros as the most important agile ritual—more so than standups or backlog groomings. Here’s why: standups keep you informed about today’s work. Backlog grooming ensures you’re building the right features. But it’s the retrospective that offers the team a collective avenue for ensuring you’re moving in the right direction <em>as a team</em>.</p>

<p>Retros are where members get a safe space to voice their opinions and share ideas, get feedback on those ideas, and actually put them into action through recurring kaizens (continuous improvements). They’re where you talk about the things that don’t fit neatly into a standup: the communication breakdown that cost you three days, the deployment process that’s slowly killing morale, the junior developer who needs more pairing time but doesn’t want to speak up in front of everyone.</p>

<h2 id="what-ive-seen-go-wrong">What I’ve Seen Go Wrong</h2>

<p>So often in my career, I’ve seen teams devalue retrospectives. They only do them at the end of a project, or at the end of a fiscal quarter. I get it—retros can feel like “just another meeting,” especially when they’re run poorly or devolve into unproductive venting sessions. Teams think they’re saving time by doing them less frequently.</p>

<p>But in my opinion, that’s way too late. By the time you’re doing a quarterly retro, you’ve already built three months of technical debt, burned out half your team, and shipped features nobody wanted.</p>

<h2 id="the-cost-of-infrequent-course-corrections">The Cost of Infrequent Course Corrections</h2>

<p>To go back to the driving metaphor: you wouldn’t set your vehicle in a direction and then come back to check once you think you should’ve reached your destination, only to find out that you missed your exit three hours ago. That would be absurd.</p>

<p>Yet that’s exactly what teams do when they skip regular retros. They work heads-down for months, assuming they’re on the right path, only to discover they’ve been building in the wrong direction, using processes that frustrate everyone, and missing opportunities to improve along the way.</p>

<p>I’ve watched this play out firsthand. One team I worked with went six weeks without a retro because they were “too busy with the release.” By the time we finally sat down together, tensions were high, the deployment pipeline was a mess, and three people were quietly interviewing elsewhere. If we’d been checking in every two weeks, we could have caught and fixed those issues when they were small.</p>

<h2 id="a-better-way-the-asynchronous-retrospective">A Better Way: The Asynchronous Retrospective</h2>

<p>Here’s where I’ve been experimenting lately, and I think it solves a lot of the problems with traditional retros.</p>

<p>Instead of blocking 90 minutes on everyone’s calendar every two weeks, I’ve been running what I call <strong>asynchronous retrospectives</strong>. Using a tool like Figma (or Miro, or even a shared doc), we spin up a traditional retro board and leave it open constantly throughout the sprint. Team members can jump in ad hoc whenever they want and provide feedback—just like they would during the opening 10 minutes of a traditional synchronous retrospective.</p>

<p>This approach has several advantages:</p>

<p><strong>It amortizes the cost of the retro over the entire iteration.</strong> Instead of context-switching everyone into a meeting at once, people can contribute when it’s convenient—right after they encounter something worth noting. The feedback is fresher, more specific, and doesn’t require anyone to remember what frustrated them two weeks ago.</p>

<p><strong>It enables more candid, anonymous feedback.</strong> Even in psychologically safe environments, some people are more comfortable writing than speaking up in a room. The async format gives everyone equal voice, regardless of whether they’re introverted, remote, or just need time to articulate their thoughts.</p>

<p><strong>It allows for faster reaction.</strong> We don’t have to wait for the next scheduled retro to propose changes. We can do kaizens constantly through this approach. When someone drops a piece of feedback into the board, discussions can happen in Slack to refine what a kaizen looks like in response. Small improvements can ship within days, not weeks.</p>

<p><strong>It’s easier to coordinate.</strong> No more “sorry, I have a conflict” or “can we move it 30 minutes earlier?” The board is always there. You contribute when you can.</p>

<p>I’ve found this to be much more effective than a traditional, all-hands synchronous retrospective. That said, we do still have a brief sync every two weeks—maybe 30 minutes—to review the async board together, discuss themes, and commit to specific action items. But the heavy lifting happens throughout the sprint, not in one exhausting session.</p>

<h2 id="keep-both-hands-on-the-wheel">Keep Both Hands on the Wheel</h2>

<p>Whether you go synchronous, asynchronous, or hybrid, the key is this: you need to be doing retrospectives constantly. This is what it means to have both hands on the wheel in software development.</p>

<p>The goal isn’t to have a perfect process. The goal is to constantly adjust your trajectory. A small course correction today prevents a major detour tomorrow.</p>
<h2 id="your-turn">Your Turn</h2>

<p>When’s your next retrospective scheduled? If the answer is “I don’t know” or “not for a while,” that’s your first problem to solve.</p>

<p>And if you’re doing traditional retros but finding them draining or ineffective, consider trying an async approach. Spin up a board, share it with your team, and see what happens when you give people the freedom to contribute on their own time.</p>

<p>Because if you’re not constantly checking whether you’re pointed in the right direction, building the right things in the right way, you’re not really driving—you’re just hoping you end up somewhere good.</p>

<p>Keep both hands on the wheel.</p>]]></content><author><name>Your Name</name></author><category term="Agile" /><summary type="html"><![CDATA[There’s this metaphor that’s stuck with me for years: when you’re driving a car, you don’t just aim it in a direction and let it go. Instead, you’re constantly adjusting at the micro level—holding the wheel, turning it a micrometer to the left or to the right to make sure you’re on track.]]></summary></entry><entry><title type="html">New Coat of Paint</title><link href="https://jordanforeman.com/2025/new-coat-of-paint/" rel="alternate" type="text/html" title="New Coat of Paint" /><published>2025-11-23T00:00:00+00:00</published><updated>2025-11-23T00:00:00+00:00</updated><id>https://jordanforeman.com/2025/new-coat-of-paint</id><content type="html" xml:base="https://jordanforeman.com/2025/new-coat-of-paint/"><![CDATA[<p>It’s been almost five and a half years since my last post on this site. I’ve aspired to write a lot more over the years, but of course every time I got the impulse to do so I was presented with the disgusting monstrosity that was the website I cobbled together almost a decade ago 🤮</p>

<p>Let’s just say that my skills have improved a tad in that time. However, my free time has significantly plummeted in that time as well (three kids will have that effect).</p>

<p>So while it’s long overdue, I’ve finally been able to relaunch my personal blog with a fresh coat of paint. All the cringey old blog posts are still here, and should hopefully be buried in the paginator over the coming months. In the meantime, let’s recap all that’s changed since I last posted!</p>

<h2 id="what-ive-been-up-to">What I’ve Been up to</h2>

<p>When last I wrote, I was working as a Data &amp; Insights tech lead for the Intelligent Solutions Group at John Deere. Since then, I’ve done a few other things:</p>

<ol>
  <li>I had my first kid! <strong>2020</strong></li>
  <li>I left John Deere to lead a team at the newly formed QuickBooks Commerce group at Intuit <strong>2021</strong></li>
  <li>I had another kid! <strong>2022</strong></li>
  <li>I left Intuit for Shopify, where I’ve been working on Orders &amp; Returns for the last few years. <strong>2023</strong></li>
  <li>I had yet another kid! <strong>2025</strong></li>
</ol>

<p>So mostly making kids and popping around big tech. It’s been a fun few years, and while I’ve learned a ton I haven’t been the best at taking the time to reflect and write about the things I’ve learned. This post is my way of trying to be better about that.</p>

<h2 id="what-i-plan-to-write-about">What I Plan to Write About</h2>

<p>My focus for the past few years has been effectively leading engineering efforts at large-scale companies. As such, I’ll definitely be writing about leadership and philosophy and all other sorts of <em>soft skills</em>-esque facets of engineering.</p>

<p>I’ve also had my love of engineering reinvigorated with my introduction to the Ruby programming language. I’ll also write quite a bit about raw engineering such as design patterns, languages (Ruby and Rust are my current focuses, but JavaScript will always be my first love and act as my primary language), and systems design.</p>

<p>And of course, AI. It’s so hot right now, and I’d be lying if I said that I designed and built this new website all on my own. Our discipline is changing rapidly, and it’s important to stay on top of these developments.</p>

<h2 id="when-i-plan-to-write">When I Plan to Write</h2>

<p>I probably shouldn’t even try to predict this. However I do get the urge with some frequency, and the eyesore that was my old website is no longer a blocker. Hopefully I can bring myself to write something at least once a week, but I’m not going to force it. If I have something to say, I’ll say it. If I don’t I won’t clutter this site with garbage.</p>

<h2 id="-welcome-back">👋 Welcome (Back)!</h2>

<p>So if you’re reading this, welcome! I’m excited to get back to maintaining my own personal site. Hopefully this enthusiasm manifests! I have a few ideas for this site - both content and features - that should be coming in the next few months. The holidays are right around the corner 🎄 and this is always a fun time to dig into personal projects.</p>

<p>Until next time 🚀</p>]]></content><author><name>Your Name</name></author><category term="Meta" /><summary type="html"><![CDATA[It’s been almost five and a half years since my last post on this site. I’ve aspired to write a lot more over the years, but of course every time I got the impulse to do so I was presented with the disgusting monstrosity that was the website I cobbled together almost a decade ago 🤮]]></summary></entry><entry><title type="html">Automated Dependency Management</title><link href="https://jordanforeman.com/2020/automated-dependency-management/" rel="alternate" type="text/html" title="Automated Dependency Management" /><published>2020-08-19T00:00:00+00:00</published><updated>2020-08-19T00:00:00+00:00</updated><id>https://jordanforeman.com/2020/automated-dependency-management</id><content type="html" xml:base="https://jordanforeman.com/2020/automated-dependency-management/"><![CDATA[<p>Managing dependency versions for your applications and modules can be a time consuming pain in the bum. Not only must you keep tabs on the release cycles of all of your dependencies, but you must then assess each release invidually, manually update the versions in all the consuming applications you own, and make any changes required to support these latest versions - every. single. time.</p>

<p>Luckily as software development practices and the dependency ecosystems we use have matured, we’ve seen the advent of several automation tools that allow us to simplify this burdensome process into something far more manageable.</p>

<h2 id="why-manage-dependencies-at-all">Why Manage Dependencies At All?</h2>

<p>I’ve already highlighted some of the pain points in keeping dependencies up-to-date, but I haven’t really explained why you should even care to do so in the first place. When I suggest the need to keep dependencies up-to-date, I’m regularly met with the same refrain:</p>

<blockquote>
  <p>It works with the current version, so I won’t even bother. I’ll update when I need a new version.</p>
</blockquote>

<p>This is an understable - albeit misguided - position for several reasons:</p>

<p>Practically speaking, by the time you find yourself needing functionality exposed in newer versions of any given dependency, you risk finding yourself several breaking versions behind. This all too common situation artificially increases the burden of incorporating newer versions by requiring several unrelated changes to the existing codebase - just to get access to a single version (which itself may not actually require any changes at all).</p>

<p>Another - more nebulous - concern is that of security. Any given version may be <em>functionally</em> adequate for the given state of your application, but security vulnerabilities are discovered and patched every day. By neglecting to keep your dependency tree up to date you put your application, and therefore your users, at risk. This is perhaps the most critical reason to keep your dependencies up to date.</p>

<h2 id="automatic-versioning">Automatic Versioning</h2>

<p>Hopefully by now you’re convinced that keeping your dependencies up to date is an important task. However, we also know that it is by no means a trivial one. Keeping dependencies up-to-date could be a full-time job itself depending on the scope of your ownership. That said there are several tools we can leverage to make this process easier.</p>

<h3 id="tools">Tools</h3>

<h4 id="semver">SemVer</h4>

<p>Perhaps the most important concept to build upon is that of <strong><a href="https://semver.org">Semantic Versioning</a></strong> (or SemVer). Semantic Versioning is a practice that aims to imbue any given version of a code module with an explicit meaning that correlates to the code itself. When releasing new versions of modules that you maintain, its important to adhere to this standard so as to better communicate with those who consume your work.</p>

<p>In practice, Semantic Versioning manifests itself as three different release <em>categories</em>, each indicated by the various pieces of a version tag (you may be familiar with the syntax: <code class="language-text highlighter-rouge">v1.2.3</code>). Let’s look at those in more depth:</p>

<blockquote>
  <p><strong>Example:</strong> <code class="language-text highlighter-rouge">v.1.2.3</code></p>
</blockquote>

<h5 id="major">Major</h5>

<p>A <strong>Major</strong> version release is often the least common. This is because a major version change indicates that something, well, <em>major</em> has changed with the underlying code that necessitates consumers modify their consumption thereof. This is often referred to as a <em>“Breaking”</em> change, because without actual changes to the accomodate them, these changes will cause the consuming application to not work (be broken).</p>

<p>In the example above, <code class="language-text highlighter-rouge">1</code> is the major version.</p>

<h5 id="minor">Minor</h5>

<p>A <strong>Minor</strong> version release indicates that the module has changed in such a way that any consumer should be able to update without having to make any changes to their existing codebase. These minor versions, however, likely contain new features that one may wish to make use of in our applications.</p>

<p>In the example above, <code class="language-text highlighter-rouge">2</code> is the minor version.</p>

<h5 id="patch">Patch</h5>

<p>A <strong>Patch</strong> version release is the most common. Patches indicate changes to the module code that should be invisible to those consuming it. These sorts of changes include (but are not limited to): bugfixes, security vulnerability patches, and performance optimizations.</p>

<p>In the example above, <code class="language-text highlighter-rouge">3</code> is the patch version.</p>

<hr />

<p>It’s worth acknowledging that the version definitions above are flexible. It may be impossible to address a given security vulnerability without introducing a breaking change to a codebase, and therefore a major version change may be required for what might intuitively be considered a patch. Having some awareness of how your code is used by others is a critical facet of maintaining shared modules.</p>

<h4 id="semantic-collaboration">Semantic Collaboration</h4>

<p>Understanding what semantic versions mean is one thing, but having to make this decision every time you wish to release a version of your application can be a nightmare - especially if you’re not the only developer on the project. In that scenario, how do you know what category is correct for releasing your code changes if that release will also include changes from others? This can lead to many wasted hours of effort simply communicating between developers who may not even inhabit the same time zone!</p>

<p>One excellent way to ensure that this communication can be done quickly and without coordinating disparate human beings is to use Semantic Commits. Semantic Commits aim to make Git commits convey meaning similar to the way that Semantic Versioning conveys meaning of releases. With semantic commits, each commit message is prefixed with a common keyword that indicates to other developers what that change includes. Examples of semantic commit prefixes include (but are not limited to):</p>

<ul>
  <li><code class="language-text highlighter-rouge">feat</code> for commits that introduce new features</li>
  <li><code class="language-text highlighter-rouge">fix</code> for commits that fix bugs or security vulnerabilities</li>
  <li><code class="language-text highlighter-rouge">perf</code> commits introduce performance improvements</li>
</ul>

<p>Semantic commit prefixes are entirely subjective. The goal is simply to foster better asynchronous communication between developers. Establishing a shared language with your collaborators is crucial. That said, you’ll often find teams gravitate towards using the prefixes established by <a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153">the Angular Project</a> due to the network effect established by the various automation tools that build on them.</p>

<p>One such tool is <a href="https://semantic-release.gitbook.io/semantic-release/">Semantic Release</a>. Semantic Release will automatically publish new releases of your code, and is typically executed on every merge to the <code class="language-text highlighter-rouge">master</code> branch. Semantic Release builds on Semantic Commits by using a codified set of prefixes to determine what semantic level of release to publish automatically.</p>

<p>Publishing a new release on every merge to the <code class="language-text highlighter-rouge">master</code> branch allows new versions to be concise in what they chance and reduces the amount of unpublished inventory. More frequent releases, however, also wind up creating far more releases than one may be used to. While this provides consumers far more control over what code changes to incorporate into their applications, it also increases the frequency with which that decision must be made. Wouldn’t it be great if we could automate that as well?</p>

<h2 id="automatic-upgrades">Automatic Upgrades</h2>

<p>As SemVer has rapidly taken over the world of open-source software development (thanks in no small part to the many practices outlined above), even more tools have been created that build on the assumption of SemVer to simplify consumption as well as maintenance of open source software. I’ll outline two popular tools below: WhiteSource’s Renovate and GitHub’s Dependabot.</p>

<h3 id="renovate">Renovate</h3>

<p>Long the reigning champ of automated dependency management, <a href="https://renovate.whitesourcesoftware.com/">Renovate</a> (recently purchased by WhiteSource Software) is an application that runs on its own dedicated compute resources and scans source code repositories (think Github, GitLab, Bitbucket, etc.) that specify their dependencies in code (<code class="language-text highlighter-rouge">package.json</code>, <code class="language-text highlighter-rouge">pom.xml</code>, etc.) and then leverages public module repositories (npm, maven, etc.) to know when a dependency needs updated.</p>

<p>If you’re using GitHub to host your application code, Renovate will open a Pull Request to your application whenever a new version of a dependency is released. This completely removes the burden of having to track dependencies from developers. Simply monitor you own codebase - you’re already doing that, right? - for pull requests from Renovate’s bot, and merge away! With some <a href="https://docs.renovatebot.com/">simple configuration</a>, you can even configure GitHub + Renovate to automatically merge these pull requests without you having to even verify them yourselves.</p>

<p>Be sure to only automerge minor or patch releases! Major versions require other changes that require an understanding of your application that simply cannot be automated.</p>

<h3 id="dependabot">Dependabot</h3>

<p>Perhaps less well known, Dependabot is an up-and-coming tool for automating updates to dependencies. After being acquired by GitHub, Dependabot has become far more prevalent within the GitHub ecosystem as a tool of first resort when approaching security. If you’re using public GitHub, you’ll likely have noticed the new “Security” tab on some of your code repositories; this is powered by Dependabot!</p>

<p>The fact that Dependabot’s adoption into the GitHub suite of tools is maturing rapidly, its quickly becoming a qualified alternative to tools such as Renovate. With its security integrations being baked into the GitHub interface, and enterprise support coming soon, it’s definitely worth giving it a shot if you’re just getting started with automated dependency management or are still using Renovate.</p>

<h2 id="a-word-of-caution">A Word of Caution</h2>

<p>As with any automation, the lack of manual oversight presents its own concerns. While systems and tools like SemVer <em>aim</em> to enforce a strict definition of dependencies that indicate consumption requirements, its entirely possible that erroneous release versions can occur - eg. it is entirely possible that a <em>minor</em> release version can include breaking changes. By automatically consuming this minor version without modifying our consumption, we risk breaking our application quickly and without warning.</p>

<p>The best way to mitigate this possibility is the same way we mitigate any other potentially destructive change to our applications: with a robust automated testing ecosystem. By automatically ensuring the integrity of our applications on every single change, we’re able to confortably allow automation to assume more and more responsibilities so that we may focus on driving value for our business.</p>]]></content><author><name>Your Name</name></author><category term="engineering" /><summary type="html"><![CDATA[Managing dependency versions for your applications and modules can be a time consuming pain in the bum. Not only must you keep tabs on the release cycles of all of your dependencies, but you must then assess each release invidually, manually update the versions in all the consuming applications you own, and make any changes required to support these latest versions - every. single. time.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jordanforeman.com/dependency-graph.png" /><media:content medium="image" url="https://jordanforeman.com/dependency-graph.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why Not Both?</title><link href="https://jordanforeman.com/2020/why-not-both/" rel="alternate" type="text/html" title="Why Not Both?" /><published>2020-04-30T00:00:00+00:00</published><updated>2020-04-30T00:00:00+00:00</updated><id>https://jordanforeman.com/2020/why-not-both</id><content type="html" xml:base="https://jordanforeman.com/2020/why-not-both/"><![CDATA[<p>Its an old meme from a TV commercial where a family is fighting about whether or not to eat hard shell or soft shell tacos; the young girl asks “why don’t we have both?” and is celebrated for being a productive mediator</p>

<iframe width="560" height="315" style="display:block;margin:0 auto;" src="https://www.youtube.com/embed/vqgSO8_cRio" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Its my profile picture because I often find myself saying the same thing in technical/design/product conversations. Many times as developers, designers, POs, and leaders we’re faced with decisions that are presented as binary, but aren’t actually mutually exclusive - with a little creativity, we can often have both.</p>]]></content><author><name>Your Name</name></author><category term="leadership" /><summary type="html"><![CDATA[Its an old meme from a TV commercial where a family is fighting about whether or not to eat hard shell or soft shell tacos; the young girl asks “why don’t we have both?” and is celebrated for being a productive mediator]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jordanforeman.com/why-not-both.jpg" /><media:content medium="image" url="https://jordanforeman.com/why-not-both.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fargate vs. Lambda</title><link href="https://jordanforeman.com/2019/fargate-vs-lambda/" rel="alternate" type="text/html" title="Fargate vs. Lambda" /><published>2019-07-22T00:00:00+00:00</published><updated>2019-07-22T00:00:00+00:00</updated><id>https://jordanforeman.com/2019/fargate-vs-lambda</id><content type="html" xml:base="https://jordanforeman.com/2019/fargate-vs-lambda/"><![CDATA[<p>With serverless architectures being all the rage these days, you’re probably wondering what options exist for your application in this brave new world.</p>

<p>If you develop primarily in the AWS ecosystem, you’re likely already aware of Lambda functions and how tools such as API Gateway allow us to instrument fully-functional microservices while maintaining absurdly low operational costs (or absurdly high, if your service is popular!) What you might be less familiar with, but are perhaps curious about (you did open this blog post, after all), is AWS’s Fargate solution.</p>

<p>This post attempts to describe with some detail the two aforementioned architectural solutions with a focus on what distinguishes the two from one another, and ultimately help drive your decision to the right tool for the job at hand.</p>

<h2 id="lambda">Lambda</h2>

<p>As mentioned previously, AWS Lambda functions have (right or wrong) become almost synonymous with “serverless” in the minds of most developers. But if we consult the excellent (albeit curiously branded) <a href="https://serverless.com/">serverless.com</a>, we see a definition of “serverless” where the term “lambda” is conspicuously absent:</p>

<blockquote>
  <p>Just like wireless internet has wires somewhere, serverless architectures still have servers somewhere. What ‘serverless’ really means is that, as a developer you don’t have to think about those servers. You just focus on code.</p>
</blockquote>

<p>After all, Serverless as a philosophy isn’t limited to AWS (think Microsoft’s Azure Functions as a Lambda equivalent), and so why should any single tool be representative of the philosophy?</p>

<p>That said, this common misconception isn’t <em>entirely</em> without merit. Serverless functions provide an easy-to-understand implementation of a serverless microservice that many of the world’s most profitable companies leverage in production today.</p>

<p>Just as not all tissues are “Kleenex”, so too are not all serverless functions “lambdas” - the key to understanding each is to focus on the platonic form, rather than the brand. Lambdas are simply “functions” that execute in an environment where you as a developer don’t have to concern yourself with the underlying server. That’s it.</p>

<p>Because Lambdas are boiled down to the lowest functional unit of an application (a function), much of the traditional concerns an application developer needs to take into account are managed for you: runtime, OS, memory, etc. are largely defined by AWS and you’re free to implement your function on top of it.</p>

<p>This simplistic abstraction is what really sets Lambda apart from other, more holistic approaches such as…</p>

<h2 id="fargate">Fargate</h2>

<p>Like Lambdas, Fargate (either via ECS or Kubernetes) allow us as developers to remove from scope concerns related to the physical “server” itself: things such as system memory, CPU usage, etc. Again, these things are still in use, but they are entirely managed by Jeff Bezos’ team of highly skilled systems specialists. We get to focus on our code!</p>

<p>A keen eye, however, will notice some glaring differences between the two lists of non-concerns layed out in the above few paragraphs:</p>

<p>Whereas Lambdas remove concerns related to OS and runtime <em>as well as</em> “baremetal” concerns like CPU and memory, Fargate only handles the latter. In this way, Fargate is still <em>technically</em> serverless (and allows us to harvest the fiscal benefits of <strong>not</strong> running a server constantly), but still enables us to define the minutea of our code’s runtime environment through the use of Containers.</p>

<p>Let’s say, for example, that you’re building an application using Node.js. If you want to run this application via a series of Lambdas, you’re locked in to <a href="https://docs.aws.amazon.com/lambda/latest/dg/programming-model.html">the two Node runtimes that AWS provides</a> for Lambdas: <code class="language-text highlighter-rouge">Node@8</code> and <code class="language-text highlighter-rouge">Node@10</code>. Want to run <code class="language-text highlighter-rouge">Node@12</code>? Not an option. <em>Need</em> to run something as old as <code class="language-text highlighter-rouge">Node@6</code>? Not an option (anymore). Fargate allows you to specify <em>exactly</em> what your runtime environment looks like by using whatever Docker container you provide, and therein lies the key difference.</p>

<h2 id="cold-starts">Cold Starts</h2>

<p>So we know the key differences between these two tools, but I’d like to focus for a moment on one of their key similarities.</p>

<p>As mentioned before, both are <em>technically</em> serverless in that they satisfy the definition quoted earlier. Both run on-demand, and as such will shut down after a predetermined amount of idle use, saving you (and/or your company) precious capital.</p>

<p>Whenever a serverless “application” or “function” shuts down, however, it is not longer as immediately accessible as it was when it was alive and running. This is what saves you money, but with the caveat that it makes triggers to your serverless application during the “down” state measurably slower, as the responses to those triggers are blocked by the time it takes to start up the environment in which the process runs. This is a common problem in serverless architectures, and there are several ways to address this problem.</p>

<p>The key to managing cold-starts is to understand usage patterns of the service you’re trying to build. If its going to be high-usage, then you’ll likely not have to worry much about cold-starts because there will likely be long periods of “warm” servers servicing your requests - that said, if you’re relying on frequent traffic to keep your serverless servers warm, you’re likely toeing the line where it might make sense to pay for a constantly running compute instance (server, as opposed to serverless) for your architecture. This is an important decision that only you can make, and is outside the scope of this post.</p>

<h2 id="when-to-use-fargate-over-lambda">When to use Fargate over Lambda</h2>

<p>Assuming you’ve made an informed decision to go serverless, you’re likely still unsure whether or not to go the “serverless functional” (lambda) route, or the “serverless container” (Fargate) route. You’ll likely want to pick Fargate if:</p>

<ul>
  <li>You need strict control over the runtime environment (Lambda only supports so much)</li>
  <li>Your application relies on a composition of multiple system level processes (such as daemons)</li>
</ul>

<p>A general rule of thumb is that you <strong>need</strong> Fargate if your application <strong>needs</strong> to be run as a container (or relies heavily on processes <em>outside</em> of your codebase).</p>

<h2 id="when-to-use-lambda-over-fargate">When to use Lambda over Fargate</h2>

<p>Given that last statement, if the scope of your application is isolated to running the business logic you’ve defined in your codebase, you’re likely just fine with Lambdas. You’ll likely want to pick Lambdas if:</p>

<ul>
  <li>You simply want to execute business logic, and don’t rely on other processes running on the system</li>
  <li>The runtimes provided by AWS are sufficient to do so</li>
</ul>

<h2 id="in-conclusion">In Conclusion</h2>

<p>To be fair, this post really only captures the basics of Lambdas vs Fargate, and the considerations I’ve personally taken into account when architecting solutions using either one. If you’d like to learn more about Fargate, I can’t recommend enough the <a href="https://www.youtube.com/watch?v=xBgiArJHv7E">following video</a> by Abby Fuller from AWS.</p>

<iframe style="display: block; margin: 0 auto;" src="https://www.youtube-nocookie.com/embed/xBgiArJHv7E" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>]]></content><author><name>Your Name</name></author><category term="serverless" /><summary type="html"><![CDATA[With serverless architectures being all the rage these days, you’re probably wondering what options exist for your application in this brave new world.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jordanforeman.com/fargate.png" /><media:content medium="image" url="https://jordanforeman.com/fargate.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Implicit vs Explicit Tests</title><link href="https://jordanforeman.com/2018/implicit-explicit-stubs/" rel="alternate" type="text/html" title="Implicit vs Explicit Tests" /><published>2018-07-19T00:00:00+00:00</published><updated>2018-07-19T00:00:00+00:00</updated><id>https://jordanforeman.com/2018/implicit-explicit-stubs</id><content type="html" xml:base="https://jordanforeman.com/2018/implicit-explicit-stubs/"><![CDATA[<p>When writing unit tests, a developer will often find themselves needing to override internal functionality that is consumed by the code under test. This is achieved by using <a href="https://martinfowler.com/articles/mocksArentStubs.html"><strong>stubs</strong></a>.</p>

<p>Let’s say for example you want to test that a given functional unit utilizes the output of another, tangential functional unit:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">getData</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../utils/get-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">mapData</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../utils/map-data</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">function</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nf">getData</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">mapData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    
    <span class="k">return</span> <span class="nx">output</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this example, <code class="language-text highlighter-rouge">myFunction</code> utilizes <code class="language-text highlighter-rouge">getData</code> to derive a <code class="language-text highlighter-rouge">data</code> value using the former’s input. When we write unit tests for <code class="language-text highlighter-rouge">myFunction</code>, we could take one of two approaches:</p>

<ul>
  <li>Assume the logical internals of <code class="language-text highlighter-rouge">getData</code> as well as <code class="language-text highlighter-rouge">mapData</code>, and manually reproduce them in our tests to validate <code class="language-text highlighter-rouge">output</code>.</li>
  <li>Use stubs!</li>
</ul>

<h2 id="using-stubs">Using Stubs</h2>

<p>If it weren’t already clear, the answer here is definitely to use stubs. <code class="language-text highlighter-rouge">getData</code> and <code class="language-text highlighter-rouge">mapData</code> should already be tested in isolation, and duplicating that test logic is largely unnecessary. There are, of course, exceptions to this statement; however more often than not if something is imported/included/required in your code, stub it in your tests!</p>

<p>Let’s see an example of how we might test <code class="language-text highlighter-rouge">myFunction</code>:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">sinon</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">sinon</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">expect</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">;</span>

<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">GetDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/get-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">MapDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/map-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">myFunction</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/my-function</span><span class="dl">'</span><span class="p">;</span>

<span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">myFunction</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nf">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">should convert input into output</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">input</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">my input</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">expectedOutput</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">the output I expect</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">getData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">GetDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">getData</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">mapData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">MapDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">mapData</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="nx">getData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
        <span class="nx">mapData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">output</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>This is a pretty basic test utilizing stubs. We’re importing both <code class="language-text highlighter-rouge">getData</code> and <code class="language-text highlighter-rouge">mapData</code> and using <a href="http://sinonjs.org/"><code class="language-text highlighter-rouge">sinon.js</code></a> to stub them. Now when we invoke <code class="language-text highlighter-rouge">myFunction</code> from the context of our test, neither <code class="language-text highlighter-rouge">getData</code> or <code class="language-text highlighter-rouge">mapData</code> will <em>actually</em> be invoked; instead, the new stub functions we’ve created will be called, and they will return what we’ve declared that they will return.</p>

<h2 id="using-stubs-correctly">Using Stubs Correctly</h2>

<p>A keen eye will notice that this test is woefully incomplete. If we were <a href="https://en.wikipedia.org/wiki/Test-driven_development">test driving</a> <code class="language-text highlighter-rouge">myFunction</code> we could easily pass the tests without even using <code class="language-text highlighter-rouge">getData</code>! In fact, we could pass the test by effectively aliasing <code class="language-text highlighter-rouge">mapData</code>:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">mapData</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../utils/map-data</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">function</span> <span class="nf">myFunction</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">mapData</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This will lead to a passing unit test, but likely won’t accomplish what we want <code class="language-text highlighter-rouge">myFunction</code> to do when run in the broader context of our application. Because we built this theoretical system we <em>know</em> that <code class="language-text highlighter-rouge">mapData</code> will only <em>actually</em> return <code class="language-text highlighter-rouge">expectedOutput</code> when its provided with <code class="language-text highlighter-rouge">data</code>and that <code class="language-text highlighter-rouge">data</code> is only available from passing <code class="language-text highlighter-rouge">input</code> into <code class="language-text highlighter-rouge">getData</code>. But how can we configure our stubs (and subsequently, our tests) to reflect this reality?</p>

<h2 id="explicitly-testing-stubs">Explicitly Testing Stubs</h2>

<p>One very common approach is to analyze the stubs <em>after</em> invoking the unit under test and verifying that they were invoked correctly:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">sinon</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">sinon</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">sinonChai</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">sinon-chai</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">chai</span><span class="p">,</span> <span class="p">{</span><span class="nx">expect</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">;</span>

<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">GetDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/get-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">MapDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/map-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">myFunction</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/my-function</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">chai</span><span class="p">.</span><span class="nf">use</span><span class="p">(</span><span class="nx">sinonChai</span><span class="p">);</span>

<span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">myFunction</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nf">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">should convert input into output</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">input</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">my input</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">expectedOutput</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">the output I expect</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">getData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">GetDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">getData</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">mapData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">MapDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">mapData</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="nx">getData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
        <span class="nx">mapData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">output</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">getData</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nf">calledWithExactly</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        <span class="nf">expect</span><span class="p">(</span><span class="nx">mapData</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nf">calledWithExactly</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>You’ll see that we’ve included the <a href="https://www.npmjs.com/package/sinon-chai"><code class="language-text highlighter-rouge">sinon-chai</code></a> package and configured <code class="language-text highlighter-rouge">chai</code> to use it. This module exposes some new assertions in <code class="language-text highlighter-rouge">chai</code> that we can use for this sort of explicit stub testing. Its certainly not the only way to do so, however.</p>

<p>This test is a lot better, but again, exposes a potential for abuse in our production code:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">getData</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../utils/get-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">mapData</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../utils/map-data</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">function</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">getData</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
    
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nf">getData</span><span class="p">();</span>
    
    <span class="nf">mapData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    
    <span class="k">return</span> <span class="nf">mapData</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This code would pass our test. <code class="language-text highlighter-rouge">output</code> (in the context of our test) <em>is</em> the return value of <code class="language-text highlighter-rouge">mapData</code>, <code class="language-text highlighter-rouge">mapData</code> <em>is</em> invoked with the output of <code class="language-text highlighter-rouge">getData</code>, and <code class="language-text highlighter-rouge">getData</code> <em>is</em> invoked with <code class="language-text highlighter-rouge">input</code>. All of our expectations are met, but there’s nothing forcing them to be “glued” together.</p>

<p>You’ll notice that we’re only able to skirt around our business requirements and still pass the tests by making arbitrary invocations of our <code class="language-text highlighter-rouge">getData</code> and <code class="language-text highlighter-rouge">mapData</code> utilities. Luckily, <code class="language-text highlighter-rouge">sinon</code> stubs come with a property we can observe that can force into the right direction:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">myFunction</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nf">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">should convert input into output</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">input</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">my input</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">expectedOutput</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">the output I expect</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">getData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">GetDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">getData</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">mapData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">MapDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">mapData</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="nx">getData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
        <span class="nx">mapData</span><span class="p">.</span><span class="nf">returns</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">output</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">getData</span><span class="p">.</span><span class="nx">callCount</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="nf">expect</span><span class="p">(</span><span class="nx">getData</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nf">calledWithExactly</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">mapData</span><span class="p">.</span><span class="nx">callCount</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="nf">expect</span><span class="p">(</span><span class="nx">mapData</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nf">calledWithExactly</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Now our test fail again, because those arbitrary invocations of our utilities will cause our <code class="language-text highlighter-rouge">callCount</code> checks to fail.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nf">getData</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">mapData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    
    <span class="k">return</span> <span class="nx">output</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>All of this is a roundabout way of forcing the correct interaction with stubbed dependencies. This was a lot of work to validate something that (I would consider) to be tangential to the <em>actual</em> business concerns that <code class="language-text highlighter-rouge">myFunction</code> was originall built to address. Let’s see if there’s a simpler approach that doesn’t conflate testing our units with testing what <em>exactly</em> goes on inside of them.</p>

<h2 id="implicitly-testing-stubs">Implicitly Testing Stubs</h2>

<p><code class="language-text highlighter-rouge">Sinon</code> offers a lot of ways to interact with stubs. Whether its by observing the number of times they’ve been called to enforcing a specific return value, if its something you want to know about or do with a function, <code class="language-text highlighter-rouge">sinon</code> stubs probably have your back.</p>

<p>While we’re on the subject of forcing a return value from a stub, wouldn’t it be great if we could make that return value conditional? What if <code class="language-text highlighter-rouge">getData</code> <em>only</em> returned <code class="language-text highlighter-rouge">data</code> when it was provided with <code class="language-text highlighter-rouge">input</code>? What if <code class="language-text highlighter-rouge">mapData</code> <em>only</em> returned <code class="language-text highlighter-rouge">output</code> when it was provided with <code class="language-text highlighter-rouge">data</code>? Introducing: <code class="language-text highlighter-rouge">.withArgs</code>.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">expectedInput</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">some input</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">expectedOutput</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">expected output</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">myStub</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">();</span>

<span class="nx">myStub</span><span class="p">.</span><span class="nf">withArgs</span><span class="p">(</span><span class="nx">expectedInput</span><span class="p">).</span><span class="nf">returns</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>

<span class="nf">myStub</span><span class="p">();</span> <span class="c1">// returns undefined</span>
<span class="nf">myStub</span><span class="p">(</span><span class="nx">expectedInput</span><span class="p">);</span> <span class="c1">// returns 'expected output'</span>
</code></pre></div></div>

<p>Using <code class="language-text highlighter-rouge">.withArgs</code>, we can configure our dependencies seperately from our actual unit test parameters. In this way, our unit tests reads as if its actually testing the <em>business logic</em> under test, and not enforcing some very specific implementation upon the developer writing code to satisfy our tests:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">sinon</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">sinon</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">expect</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">;</span>

<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">GetDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/get-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="o">*</span> <span class="nx">as</span> <span class="nx">MapDataUtil</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/utils/map-data</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">myFunction</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../src/my-function</span><span class="dl">'</span><span class="p">;</span>

<span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">myFunction</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nf">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">should convert input into output</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">input</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">my input</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">expectedOutput</span> <span class="o">=</span> <span class="nc">Symbol</span><span class="p">(</span><span class="dl">'</span><span class="s1">the output I expect</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">getData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">GetDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">getData</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">const</span> <span class="nx">mapData</span> <span class="o">=</span> <span class="nx">sinon</span><span class="p">.</span><span class="nf">stub</span><span class="p">(</span><span class="nx">MapDataUtil</span><span class="p">,</span> <span class="dl">'</span><span class="s1">mapData</span><span class="dl">'</span><span class="p">);</span>
        
        <span class="nx">getData</span><span class="p">.</span><span class="nf">withArgs</span><span class="p">(</span><span class="nx">input</span><span class="p">).</span><span class="nf">returns</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
        <span class="nx">mapData</span><span class="p">.</span><span class="nf">withArgs</span><span class="p">(</span><span class="nx">data</span><span class="p">).</span><span class="nf">returns</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
        
        <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="nf">myFunction</span><span class="p">(</span><span class="nx">input</span><span class="p">);</span>
        
        <span class="nf">expect</span><span class="p">(</span><span class="nx">output</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nf">equal</span><span class="p">(</span><span class="nx">expectedOutput</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>You’ll likely notice that there is now nothing enforcing that we don’t use <code class="language-text highlighter-rouge">getData</code> and <code class="language-text highlighter-rouge">mapData</code> over and over again. No <code class="language-text highlighter-rouge">callCount</code> validation here. As long as <code class="language-text highlighter-rouge">myFunction</code> returns <code class="language-text highlighter-rouge">expectedOutput</code>, this test will pass.</p>

<p>This is where things get a little subjective. I would argue the case that we shouldn’t care. Our dependencies (<code class="language-text highlighter-rouge">getData</code> and <code class="language-text highlighter-rouge">mapData</code>) are configured to act as they would in production. <code class="language-text highlighter-rouge">input</code> and <code class="language-text highlighter-rouge">output</code> are both enforced <em>explicitly</em>. How one gets from point A (<code class="language-text highlighter-rouge">input</code>) to point B (<code class="language-text highlighter-rouge">output</code>) is enforced <em>implicitly</em> by the configuration of our stubs. Any superfluous usage of our dependencies should be cleaned up by refactoring (a process that should be separate from TDD).</p>

<h2 id="make-up-your-own-mind">Make Up Your Own Mind</h2>

<p>Again, I’m of the opinion that this is how tests <em>should</em> be written. This by no means is to suggest that this is the <em>only</em> way tests can be written, nor that its the right approach for everyone.</p>

<p>Tests are a good way to express expectations of production code. They’re especially useful if you’re pair-programming in an environment where one person writes tests and the other writes code to “beat” those tests. How we express those expectations to both current and future developers is incredibly important, and isn’t a decision that should be taken lightly.</p>

<p>Choose wisely.</p>]]></content><author><name>Your Name</name></author><category term="javascript" /><summary type="html"><![CDATA[When writing unit tests, a developer will often find themselves needing to override internal functionality that is consumed by the code under test. This is achieved by using stubs.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jordanforeman.com/javascript.jpg" /><media:content medium="image" url="https://jordanforeman.com/javascript.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>