<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Ben’s Guide to Software Development]]></title><description><![CDATA[a thousand little improvements]]></description><link>https://bensguide.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png</url><title>Ben’s Guide to Software Development</title><link>https://bensguide.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 19 Jun 2026 19:25:19 GMT</lastBuildDate><atom:link href="https://bensguide.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ben Christel]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[bensguide@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[bensguide@substack.com]]></itunes:email><itunes:name><![CDATA[Ben Christel]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ben Christel]]></itunes:author><googleplay:owner><![CDATA[bensguide@substack.com]]></googleplay:owner><googleplay:email><![CDATA[bensguide@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ben Christel]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[AI is a lot of fun]]></title><description><![CDATA[The most positive thing I can say about &#8220;AI&#8221; coding tools is &#8212; and I mean this sincerely &#8212; that they are a lot of fun to play with.]]></description><link>https://bensguide.substack.com/p/ai-is-a-lot-of-fun</link><guid isPermaLink="false">https://bensguide.substack.com/p/ai-is-a-lot-of-fun</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Sat, 13 Jun 2026 12:01:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The most positive thing I can say about &#8220;AI&#8221; coding tools is &#8212; and I mean this sincerely &#8212; that they are a lot of fun to play with.</p><p>That&#8217;s really all you need to know to understand the hype around AI. AI is fun to play with &#8212; addictively so. It&#8217;s essentially a slot machine. You put some money in, pull the lever, and some code that <em>might</em> be useful comes out. It probably won&#8217;t actually be what you wanted. It will almost certainly have flaws. But it&#8217;s conceivable, and indeed <em>mathematically possible</em>, that it <em>might </em>be perfect. That tantalizing possibility keeps us pulling the lever again and again.</p><p>There&#8217;s some geek-specific psychology going on too. LLMs are tools that <em>seem very powerful</em> and yet, at the same time, are <em>observably imperfect</em>. That combination is catnip for computer geeks of all stripes. Our absolute favorite thing to do is take an imperfect system, tinker with it or build a wrapper around it, and make it work better. And LLMs give us <em>no end</em> of opportunities to tinker.</p><p>The tinkering instinct is, I think, what Richard Gabriel was talking about when he wrote &#8220;<a href="https://dreamsongs.com/RiseOfWorseIsBetter.html">The Rise of Worse is Better.</a>&#8221; The core idea of the worse-is-better philosophy is that if you want to make successful software, you shouldn&#8217;t try to release a perfect product. Instead, release something simple and easy to tinker with, that solves at least <em>part</em> of the problem. If it&#8217;s useful, a whole community of geeks will spring up around it, eager to sand down its rough edges. The result will be software that fits their needs better than anything you could have designed upfront.</p><p>&#8220;Worse is better&#8221; says, in essence, this: approaching software design as an engineering problem is very expensive. But if you instead approach it as a psychological problem &#8212; asking not &#8220;what should we build&#8221; but &#8220;how do we get programmers to work on this&#8221; &#8212; you can create a lot of value at little cost to yourself. We could summarize the traditional worse-is-better business strategy as:</p><ol><li><p>Release imperfect software</p></li><li><p>Get programmers to work on it for cheap</p></li><li><p>Sell the software</p></li><li><p>Profit!</p></li></ol><blockquote><p>Here&#8217;s the secret that every successful software company is based on: You can domesticate programmers the way beekeepers tame bees.  You can&#8217;t exactly communicate with them, but you can get them to swarm in one place and when they&#8217;re not looking, you can carry off the honey.</p><p>&#8212;<a href="https://www.netjeff.com/humor/item.cgi?file=DeveloperBees">Orson Scott Card</a></p></blockquote><p>AI companies like Anthropic have taken the same psychological trick and applied it in a slightly different and arguably simpler way:</p><ol><li><p>Release imperfect software</p></li><li><p>Charge money for the privilege of trying to improve it</p></li><li><p>Profit!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p></li></ol><p>In this new regime, the &#8220;improved&#8221; software is never sold, because it doesn&#8217;t have to be. What the AI companies realized is that there is so much surplus in the system that the worse-is-better process doesn&#8217;t even have to produce valuable software; the geeks will show up regardless. Geeks tinker just because they like to tinker. <strong>Software development is entertainment</strong>, and when something is entertainment you can simply charge money for it directly.</p><p>You can see the effects of this strategy playing out at almost any software company. For instance, my coworkers have written dozens of &#8220;skills&#8221; for Claude Code that sit on the shelf unused. Developing useful software is not the point of all this. <strong>The process is the product. The point is to have fun.</strong></p><p>A couple months ago, I spent several weeks building my own &#8220;agentic&#8221; LLM harness. Perhaps I&#8217;ll write about it in greater detail at some point; for now, suffice it to say that the results I get from it are <em>better than vanilla Claude Code</em> but <em>still not good enough for production</em>, which I guess should tell you something about how good I think Claude Code is.</p><p>Of course I bootstrapped the whole project using AI. After about 6 weeks of committing 90% LLM-written code, I had to go over the codebase with a fine-toothed comb, fixing all the weird issues the LLM left behind.</p><p>Just last week, I tried to use my custom harness to make <a href="https://github.com/Khan/perseus/pull/3656/">a large (~1000 line) change to an open-source codebase</a>. On the face of it, it should have been straightforward: I changed one of the core data types and told the AI to fix up all the code to match the new types. However, in spite of the apparent simplicity of the task, the AI couldn&#8217;t hack it. The resulting pull request had so many issues that I decided to redo it by hand. <a href="https://github.com/Khan/perseus/pull/3656/">You can take a look at the AI-generated PR here</a>, and at <a href="https://github.com/Khan/perseus/pull/3760">my manual rewrite here</a>. The FIXME comments in the AI-generated pull request are written by me; they are how I communicate my desired changes to the LLM. You can see where I went a little crazy reviewing the AI&#8217;s crappy code and just <a href="https://github.com/Khan/perseus/pull/3656/changes#diff-6809bbb5b5878e054e5563f2c15952de58734069b585dd00369956566b4e8018R96">started screaming at it</a>.</p><p>All that&#8217;s to say, the &#8220;productivity&#8221; benefits from AI remain elusive. However, I can conclusively and authoritatively state that <strong>it is a lot of fun</strong>! Building my own LLM harness was the most fun I&#8217;ve had writing code in years. I actually stayed up late one night working on it, which for me is unheard of. Even using it is fun, in spite of its flaws. When it produces something useful, I get a big dopamine hit. <em>I made the machine do a thing</em>. When its code is crap, I get to feel superior as I fix all its weird goof-ups. It&#8217;s win-win&#8230; at least, as long I&#8217;m not the one paying for tokens.</p><p>My coworkers are now talking about &#8220;loops&#8221; as a technique for getting better outputs from LLMs. This unlikely buzzword is the current stalwart defender of the AI hype fortress, its predecessors &#8212; &#8220;prompt engineering,&#8221; &#8220;MCP,&#8221; &#8220;skills,&#8221; &#8220;context engineering,&#8221; &#8220;harness engineering&#8221; &#8212; littering the field like so many corpses. These techniques didn&#8217;t fix the problems of &#8220;hallucinations&#8221; or &#8220;misalignment,&#8221; and I would bet money that &#8220;loops&#8221; won&#8217;t magically fix everything either. I predict they <em>will</em> cost a lot of tokens, though.</p><p>But again, this is <em>all a lot of fun</em>, and that is the reason we&#8217;re doing it. That&#8217;s why we&#8217;re riding around this buzzword carousel again and again. Not because it makes us faster or more productive. Not because we write better code this way. Not because the software that comes out is better. <a href="https://www.wheresyoured.at/ai-doesnt-have-roi/">Not because there&#8217;s any ROI</a>. <em>Because it&#8217;s fun</em>.</p><p>Everyone I&#8217;ve talked to who is using AI has confirmed this, in the end. I ask probing questions about what techniques they&#8217;re using, what benefits they&#8217;re getting, what the quality of the resulting software is, and how long it takes to fix the issues, and eventually they admit it: <strong>&#8220;it&#8217;s just more fun to do it this way.&#8221;</strong></p><p>And that ends the conversation. For fun is beyond critique, beyond reason, beyond all economic analysis. It is unanswerable and unassailable. Fun will stand forever; it will triumph in every debate; it will still be undefeated when the last lights of our civilization wink out.</p><p>How much longer will our employers foot the bill for fun? Are they going to just let us have fun forever? When they cut off the infinite supply of tokens, will we still know how to program? Will any of our software still work? And how long will it take us to clean up the mess the LLMs left behind?</p><p>How much value are we willing to destroy in the name of having fun?</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Note: AI is not actually profitable; no one is making money except Nvidia. OpenAI and Anthropic are giant cash incinerators, even while they charge <a href="https://archive.ph/SIEfd">prices that make tech CEOs balk</a>.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Some Questions Driving Empirical Software Development]]></title><description><![CDATA[empirical (adj.)]]></description><link>https://bensguide.substack.com/p/some-questions-driving-empirical</link><guid isPermaLink="false">https://bensguide.substack.com/p/some-questions-driving-empirical</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Fri, 14 Nov 2025 12:03:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><strong>empirical</strong> (adj.)</p><ol><li><p>Pertaining to or based on experience, as opposed to theory.</p></li><li><p>Pertaining to, derived from, or testable by observations made using the physical senses or using instruments which extend the senses.</p></li><li><p>Verifiable by means of scientific experimentation.</p></li></ol><p>&#8212;<a href="https://en.wiktionary.org/wiki/empirical">Wiktionary</a></p></blockquote><p>Sometimes I do <a href="https://tidyfirst.substack.com/p/canon-tdd">test-driven development</a>, and sometimes I don&#8217;t. But no matter what process I&#8217;m using to write code, I want to stay grounded in reality, by getting frequent, low-latency feedback on my work. TDD is one way to bring empiricism into software development, and I think it&#8217;s the best way in many cases, but it&#8217;s not the only way.</p><p>Here are six questions I ask myself as I&#8217;m working &#8212; on code or anything else. These are the questions that ground an empirical development process.</p><ul><li><p><strong>What problem are we solving?</strong></p></li><li><p><strong>What is the baseline? What are we comparing our solution to?</strong></p></li><li><p><strong>How do we know the problem is actually a problem?</strong></p></li><li><p><strong>How will we know when the problem is solved?</strong></p></li><li><p><strong>Is there a cheaper solution to this problem, and </strong><em><strong>only</strong></em><strong> this problem?</strong></p></li><li><p><strong>How much do we trust our techniques for answering these questions?</strong></p></li></ul><p>I don&#8217;t always have perfectly articulated answers to these questions. But by asking them, and always looking out for better answers, I keep myself from going too far off the rails.</p><h2>What problem are we solving?</h2><p>In other words, what&#8217;s the goal? Why are we working on the system at all?</p><p>This seems like it should be a simple question to answer, but often, software gets written even though no one can clearly articulate what problem it&#8217;s meant to address.</p><blockquote><p>Step 1 is to say, &#8220;I am solving <em>this</em> problem. The problem is X and therefore we&#8217;re going to do Y.&#8221; I have seen so much software made where no one ever said that. No one ever wrote that down. And then we have a whole system, and no one ever said what problem it&#8217;s supposed to solve. If we&#8217;re not solving problems, I have no idea why we&#8217;re in this room.</p><p>&#8212;<a href="https://benchristel.github.io/yt/#https://www.youtube.com/watch?v=f84n5oFoZBc">Rich Hickey</a></p></blockquote><p>Be honest in your answer to this question. Any attempt to prevaricate will get shot down by the next question in this sequence, so just be real.</p><p>Honest answers might include:</p><ul><li><p>&#8220;This project idea is burning a hole in my brain and I can&#8217;t think about anything else.&#8221;</p></li><li><p>&#8220;I just want to learn Kotlin, and this is one way to do that.&#8221;</p></li><li><p>&#8220;We need to migrate to Next.js because it <em>might</em> solve all our infrastructure problems, and devs are complaining about legacy code, and oh god I just don&#8217;t know what else to try. At least if we switch we&#8217;ll be able to say we&#8217;re using a modern JS framework.&#8221;</p></li></ul><p>These are not necessarily bad answers. In the right context, they might represent problems worth solving!</p><p>More typical answers might include:</p><ul><li><p>&#8220;We&#8217;re fixing <em>this</em> bug &#8212; here are the repro steps.&#8221;</p></li><li><p>&#8220;We&#8217;re implementing password reset for users with email addresses.&#8221;</p></li><li><p>&#8220;We&#8217;re improving performance.&#8221;</p></li><li><p>&#8220;We&#8217;re tidying up this messy function.&#8221;</p></li></ul><h2>What is the baseline?</h2><p>In other words, what are we comparing our solution to?</p><p>In many cases, the answer to this question is self-evident: the baseline for our work is the current state of the system.</p><p>In other cases, especially when starting a new project, the baseline isn&#8217;t obvious and needs to be stated. For example, if you say &#8220;I want to write a high-performance statistics library,&#8221; the next question you should be prepared to answer is &#8220;high-performance compared to what?&#8221; What&#8217;s the fastest library out there right now? What are you trying to compete against?</p><p>Answering this question can change how you look at the problem. Maybe it turns out that there is a very fast library already available, but it doesn&#8217;t have all the features you need, or it&#8217;s poorly documented, or the API sucks. In that case, performance might not be the right focus! Maybe you can wrap the existing library in a better API, and thus solve the <em>real</em> problem.</p><h2>How do we know the problem is actually a problem?</h2><p><strong>This is the step that many programmers skip</strong> &#8212; and TDD stops you from skipping it. It&#8217;s all too easy to see a &#8220;bug&#8221; in code you just typed and swat it immediately, before you verify that it <em>is</em> a bug, and not just a piece of lint.</p><p>If you think there&#8217;s a case where the code won&#8217;t behave correctly, consider proving so with a test before you change it. If you don&#8217;t do this, at best the code will be more complicated than it needs to be. At worst, you might introduce new bugs that weren&#8217;t there in the simple code!</p><h2>How will we know when the problem is solved?</h2><p>This is another question that TDD answers for us. If we pinned down the problem with a test, then the problem is solved when the test passes.</p><p>In the absence of TDD, we should always have some way to get feedback on our work and verify that it had the desired effect. Maybe that feedback comes from other programmers, or QA, or even users &#8212; but it has to come somehow.</p><h2>Is there a cheaper solution to this problem, and <em>only</em> this problem?</h2><p>This question steers us away from writing <a href="https://ve3zsh.ca/blog/2023/09/22/big_o_of_zero.html">unnecessary code</a>, or doing any other unnecessary work. I do not mean to imply that less code is <em>always</em> better. Rather, easier-to-write code is better, <em>other things being equal</em>. Don&#8217;t reject the quick and dirty solution until you know it isn&#8217;t good enough.</p><p>The pedantic way to answer this question is to write the quick and dirty code first, and then to identify everything that&#8217;s wrong with it and to fix those problems one at a time. Quick and dirty solutions often have many problems: they might be hard to understand, or might have performance or security issues. The point is, the new problems are <em>separate</em> from our original one. By taking one problem at a time, we ensure we&#8217;re solving real problems, and not imaginary ones.</p><p>However, the pedantic way is not the only way. Sometimes, you can foresee in advance of coding that the cheap solution will be irreparably bad in some way, and will need to be ripped out and rewritten. In that case, there&#8217;s no reason to write the cheap code. Just fix the problem &#8220;in the design phase.&#8221; Whether to do this is a judgment call &#8212; as is everything.</p><h2>How much do we trust our techniques for answering these questions?</h2><p>If we&#8217;re relying on tests to tell us if our software works, our confidence in the code is limited by our confidence in the tests. That can be problematic, because tests aren&#8217;t perfect. Tests can be invalidated by mistakes in setup or assertions. And no matter how many tests we write, there will always be gaps where bugs can sneak through.</p><p>Every software verification technique has limits. This is true of static types and code review as well as tests. We can try to compensate for the weaknesses of each technique by using them in combination, and that often works very well, but we&#8217;ll never reach perfection.</p><p>I don&#8217;t worry about this too much. That&#8217;s because there are no wrong answers to the questions I&#8217;ve listed here. There&#8217;s no need to reach for some unattainable, perfect process. The important thing is to keep asking yourself the questions, and to trust that with enough repetition, your process for answering them will improve.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Structure Eats Behavior for Breakfast]]></title><description><![CDATA[Prioritizing software defects]]></description><link>https://bensguide.substack.com/p/structure-eats-behavior-for-breakfast</link><guid isPermaLink="false">https://bensguide.substack.com/p/structure-eats-behavior-for-breakfast</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Wed, 17 Sep 2025 12:05:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YQEf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Peter Drucker said that in business, &#8220;culture eats strategy for breakfast.&#8221; In software development, <strong>structure eats behavior for breakfast</strong>. Over time, structural flaws overwhelm behavioral strengths, and structural strengths cure behavioral flaws. By &#8220;behavior,&#8221; I mean &#8220;what the software does,&#8221; and by &#8220;structure,&#8221; I mean &#8220;how the code is shaped.&#8221;</p><h2>Two kinds of defects</h2><p>We can divide software defects into two broad categories: on one hand are behavioral defects &#8212; what we often call &#8220;bugs.&#8221; On the other hand are structural defects. Bugs are apparent to users of the software, but structural defects are usually invisible to everyone but the programmers. <strong>Structural defects make the software more difficult to change.</strong></p><p>Crucially, the changes impeded by bad structure include changes to improve structure! Structural quality is therefore caught in a reinforcing feedback loop:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YQEf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YQEf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 424w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 848w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 1272w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YQEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png" width="1032" height="391" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:391,&quot;width&quot;:1032,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53355,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/173785726?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YQEf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 424w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 848w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 1272w, https://substackcdn.com/image/fetch/$s_!YQEf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1862e542-6a67-47d9-9abb-7a4e1990ca9b_1032x391.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The good news is, this loop, like all reinforcing loops, works both ways. Improvements to structural quality make further improvements easier. They also make it easier to improve behavioral quality, i.e. to fix bugs or add features.</p><h2>A thought experiment</h2><p>Imagine two software systems, A and B. They have the same set of features, but:</p><ul><li><p>System A has a clear, simple structure that&#8217;s an excellent fit for its purpose. Unfortunately, there are logical errors in the code that cause it to produce incorrect results.</p></li><li><p>System B has no known bugs, and always produces correct results. Unfortunately, it has an overcomplicated structure that obscures the mechanism by which it produces those results.</p></li></ul><p>Ignore, for now, whether these systems could or would ever be built as described. The question is, <strong>which one would you rather own</strong>?</p><p>If you aren&#8217;t ever going to change the software, you&#8217;ll answer &#8220;System B.&#8221; But of course, that&#8217;s silly. Software <em>always</em> needs to change.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> And it will be much easier to fix the bugs in System A than it will be to fix the structure of System B to the point where you can develop new features safely. System A is almost certainly the better long-term choice.</p><h2>How it goes in reality</h2><p>In the real world, of course, we aren&#8217;t given a choice between System A and System B.  Structure and behavior evolve together, and bugs tend to crop up when and where the structure gets confusing. We tend to find that systems either have both good structure and good behavior, or bad structure and bad behavior.</p><p>What I&#8217;d like you to take away from this post, then, is this: <strong>efforts to fix bugs will be largely vain in the face of bad structure.</strong> When you find yourself facing a slew of bug reports, don&#8217;t focus on squashing the individual bugs. Try to understand the code first. In order to understand it, you may have to start improving the structure. Get the structural problems under control; then the bugs will pack up and leave on their own.</p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Well, <em>almost</em> always. There is one exception: <code>cat</code> is perfect and will never need to change. If your program isn&#8217;t <code>cat</code>, though, it&#8217;s going to change.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Reasons not to write bugs]]></title><description><![CDATA[What's 0.1% worth?]]></description><link>https://bensguide.substack.com/p/reasons-not-to-write-bugs</link><guid isPermaLink="false">https://bensguide.substack.com/p/reasons-not-to-write-bugs</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Sat, 06 Sep 2025 12:02:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's say you have a choice between writing code that will work 99.9% of the time, and writing code that will work 100% of the time.</p><p>What's the value of that last 0.1%?</p><ul><li><p>It will save you from a debugging nightmare later, when users report problems that you can't reproduce or diagnose.</p></li><li><p>It will prevent service outages from happening if/when the system changes so the 0.1% case is now a 10% case.</p></li><li><p>It will avoid contaminating your data with anomalies that downstream consumers will have to patch up, filter out, or (most likely) handle incorrectly &#8212; which would create bigger problems further downstream.</p></li><li><p>It will save your support team time. If your code serves a million requests per day, that one-in-a-thousand bug is going to affect a thousand people every day. A few of those people are likely to call support.</p></li><li><p>It will save your users time. Humans have terrible intuition about orders of magnitude, so do the math. If the bug affects 1000 people per day, and each person spends a minute working around your bug, that's equal to two 8-hour workdays wasted. <em>Every day</em>.</p></li><li><p>It will keep your error logs quiet, so you can quickly spot and fix <em>unexpected</em> errors instead of being distracted by <em>expected</em> errors.</p></li></ul><p>Finally, and most importantly &#8212; it's just the right thing to do. You and your users both deserve code that works.</p><h2>Oh, come on, Ben. If it were as easy as saying "just don't write bugs," software wouldn't have any bugs.</h2><p>True, we can't foresee and prevent all bugs. But we can foresee <em>some</em> bugs. (like the one I was about to write, when I started writing this post instead!)</p><p>Sometimes, we know what the "right way" is, but the "wrong way" is just so tempting. My goal in writing this post is to make it seem less tempting.</p>]]></content:encoded></item><item><title><![CDATA[Service Asymmetry]]></title><description><![CDATA[Imagine we're the programmers responsible for building a software service.]]></description><link>https://bensguide.substack.com/p/service-asymmetry</link><guid isPermaLink="false">https://bensguide.substack.com/p/service-asymmetry</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Fri, 22 Aug 2025 12:01:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LCLC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine we're the programmers responsible for building a software service. Along comes a user and asks for a new API:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LCLC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LCLC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 424w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 848w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 1272w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LCLC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png" width="524" height="330.94736842105266" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:552,&quot;width&quot;:874,&quot;resizeWidth&quot;:524,&quot;bytes&quot;:58880,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/171599066?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LCLC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 424w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 848w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 1272w, https://substackcdn.com/image/fetch/$s_!LCLC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1929e79a-8172-42e3-ab12-d72c89a083bd_874x552.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>.</p><p>The product manager records the feature request in our backlog, prioritizes it relative to the other feature requests, and we build it when it becomes the top priority.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sOqq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sOqq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 424w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 848w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 1272w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sOqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png" width="578" height="356.4240077444337" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:637,&quot;width&quot;:1033,&quot;resizeWidth&quot;:578,&quot;bytes&quot;:75751,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/171599066?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sOqq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 424w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 848w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 1272w, https://substackcdn.com/image/fetch/$s_!sOqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9dba7dc-1120-422a-b3ad-e012b3cd9c19_1033x637.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Our work is straightforward; the user is (eventually) satisfied. Peace and order reign in the land.</p><div><hr></div><p>Now, what happens when we want to <strong>remove a feature </strong>to simplify our code? Or, more generally, make <strong>breaking changes</strong> to an existing feature?</p><p>Now, rather than users asking <em>us</em> (the engineering team) to change, we're asking <em>them</em> to change. <strong>This is much harder</strong>.</p><p>Why is it harder? Well, there's one engineering team, but there's probably not just one user. Any feature that we build has a large audience, and is inevitably going to accumulate many different users.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ummg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ummg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 424w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 848w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 1272w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ummg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png" width="1128" height="833" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:833,&quot;width&quot;:1128,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/171599066?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ummg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 424w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 848w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 1272w, https://substackcdn.com/image/fetch/$s_!Ummg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d1fd58c-e1d2-4749-9fb8-f4e6f64bedae_1128x833.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Trying to get <em>many</em> users to change how they do things is much harder than getting <em>one</em> engineering team to change something. It's a simple matter of scale.</p><p>There's also the question of who is paying whom for their work. Programmers don't pay users to adapt to breaking changes. Your users might tolerate having to do a little bit of work in exchange for the ability to keep using the software, but it won&#8217;t make them happier.</p><p>Finally, the fact that breaking changes are usually motivated by technical needs rather than business needs makes it hard to get buy-in from all stakeholders. Your users have their own backlog of work they need to get done; they're not eager to add "adapting to a breaking API change" to their to-do list. If users drag their feet, who will you escalate to? Will you be able to make the case for your API change to your manager&#8217;s manager? What&#8217;s the actual business reason to drop support for the old API?</p><p>The consequence of all these factors is that <strong>it's easy to add features to software, but difficult to remove them</strong>. Every feature you add is, in effect, a promise to your users that you'll maintain that feature. It's easy to make promises, harder to keep them, and painful to break them.</p><h2>Questions to ponder</h2><ul><li><p>How might we prevent users from coupling their code to internal implementation details of our system that we want to be able to change later?</p></li><li><p>How might we give users what they need without over-promising (i.e. exposing extra APIs and data that they <em>don't</em> need)?</p></li><li><p>When we need to make breaking changes, how might we keep the old API available while users are switching over to the new one?</p></li><li><p>Incrementally adding a bunch of special-purpose APIs creates its own kind of mess. Are there design principles that can help us get a general-purpose API right the first time around?</p></li><li><p>Even if we can't get it right the first time, are there principles that could help us get it <em>wrong</em> in a way that can be made <em>righter</em> without any breaking changes?</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Steps Under the Microscope]]></title><description><![CDATA[We change a software system by taking many small steps to get from our current state to our desired state.]]></description><link>https://bensguide.substack.com/p/steps-under-the-microscope</link><guid isPermaLink="false">https://bensguide.substack.com/p/steps-under-the-microscope</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 23 Jun 2025 12:05:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZyL3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We change a software system by taking many small <strong>steps</strong> to get from our current state to our desired state. <a href="https://www.geepawhill.org/2021/10/26/mmmss-a-closer-look-at-steps/">GeePaw Hill defines a step</a> as:</p><blockquote><p>a purposeful gap of unreadiness between two points of readiness on some system&#8217;s timeline, a before point and an after point.<br><br>The system starts ready, and it ends ready, and the step is that period during which it&#8217;s not ready.</p></blockquote><p>This definition is precise, but pretty abstract, so today I'd like to talk about what <strong>steps</strong> mean to me in practice.</p><h2>An Example</h2><p>Say we&#8217;ve just created a brand new file and rolled up our sleeves to write some TypeScript. The system is in a <strong>ready</strong> state, because an empty file is perfectly valid, runnable TS, even though it doesn&#8217;t do anything.</p><pre><code></code></pre><p>I&#8217;ll represent this ready state as a blue-green square.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_aNE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_aNE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 424w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 848w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 1272w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_aNE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png" width="148" height="150.1346153846154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:211,&quot;width&quot;:208,&quot;resizeWidth&quot;:148,&quot;bytes&quot;:5043,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_aNE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 424w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 848w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 1272w, https://substackcdn.com/image/fetch/$s_!_aNE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe13fdf41-fbb1-4b8c-b76b-e4b50e5033ca_208x211.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Now we want to import our test framework so we can do some TDD. By the time we&#8217;ve typed</p><pre><code>import</code></pre><p>our system is no longer ready. This isn&#8217;t syntactically valid TypeScript, because <strong>import</strong> is a keyword and needs to be followed by whatever we&#8217;re importing. At this point, we&#8217;ve taken <em>half</em> a step. We&#8217;re up in the air.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tV_X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tV_X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 424w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 848w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 1272w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tV_X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png" width="174" height="176.05188679245282" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:429,&quot;width&quot;:424,&quot;resizeWidth&quot;:174,&quot;bytes&quot;:18104,&quot;alt&quot;:&quot;two squares side-by-side; a blue-green square on the left and a red square on the right. An arrow points up and to the right from the blue-green square, ending in midair over the red square.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="two squares side-by-side; a blue-green square on the left and a red square on the right. An arrow points up and to the right from the blue-green square, ending in midair over the red square." title="two squares side-by-side; a blue-green square on the left and a red square on the right. An arrow points up and to the right from the blue-green square, ending in midair over the red square." srcset="https://substackcdn.com/image/fetch/$s_!tV_X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 424w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 848w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 1272w, https://substackcdn.com/image/fetch/$s_!tV_X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea9c0a-6784-4333-9cc8-380acede6359_424x429.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>We finish typing the import statement, and we&#8217;re back on solid ground again. Ahhh. Relief. That&#8217;s one step.</p><pre><code>import {test, expect, is} from "@benchristel/taste"</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s_CF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s_CF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 424w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 848w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 1272w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s_CF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png" width="262" height="120.4223602484472" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d348f41-2488-45f9-afab-1844269c76f0_644x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:296,&quot;width&quot;:644,&quot;resizeWidth&quot;:262,&quot;bytes&quot;:20882,&quot;alt&quot;:&quot;a row of three squares; from left to right, green, red, green. An arrow arcs from the left green square to the right one, passing over the red square.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="a row of three squares; from left to right, green, red, green. An arrow arcs from the left green square to the right one, passing over the red square." title="a row of three squares; from left to right, green, red, green. An arrow arcs from the left green square to the right one, passing over the red square." srcset="https://substackcdn.com/image/fetch/$s_!s_CF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 424w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 848w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 1272w, https://substackcdn.com/image/fetch/$s_!s_CF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d348f41-2488-45f9-afab-1844269c76f0_644x296.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>We take a few more steps of this kind, completing our first test:</p><pre><code>import {test, expect, is} from "@benchristel/taste"

test("a Calculator", {
  "initially displays 0"() {
    const calculator = new Calculator()
    expect(calculator.display(), is, "0")
  },
})</code></pre><p>But now, a funny thing happens. We&#8217;re in a syntactically valid state, but our code doesn&#8217;t typecheck, because <code>Calculator</code> is not defined. Although we&#8217;ve taken many steps at the syntactic level, we&#8217;re now just <em>halfway </em>through a step at a higher level &#8212; the level of types.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PCTf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PCTf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 424w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 848w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 1272w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PCTf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png" width="386" height="175.95539696699376" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e28b3831-dfa2-427c-907e-282dfa124906_1121x511.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:511,&quot;width&quot;:1121,&quot;resizeWidth&quot;:386,&quot;bytes&quot;:60064,&quot;alt&quot;:&quot;Two rows of squares. The bottom row is labeled \&quot;syntax\&quot; and the top row is labeled \&quot;typechecking\&quot;. The squares in the bottom row, from left to right, are: green, red, green, red, green. Arrows arc between each pair of green squares. In the top row, there is a green square on the left and a red square on the right. An arrow points up and to the right from the green square, ending in midair over the red square.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two rows of squares. The bottom row is labeled &quot;syntax&quot; and the top row is labeled &quot;typechecking&quot;. The squares in the bottom row, from left to right, are: green, red, green, red, green. Arrows arc between each pair of green squares. In the top row, there is a green square on the left and a red square on the right. An arrow points up and to the right from the green square, ending in midair over the red square." title="Two rows of squares. The bottom row is labeled &quot;syntax&quot; and the top row is labeled &quot;typechecking&quot;. The squares in the bottom row, from left to right, are: green, red, green, red, green. Arrows arc between each pair of green squares. In the top row, there is a green square on the left and a red square on the right. An arrow points up and to the right from the green square, ending in midair over the red square." srcset="https://substackcdn.com/image/fetch/$s_!PCTf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 424w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 848w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 1272w, https://substackcdn.com/image/fetch/$s_!PCTf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe28b3831-dfa2-427c-907e-282dfa124906_1121x511.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>There are a few ways we could get back to green, but the quickest is probably to define Calculator in our current file.</p><pre><code>import {test, expect, is} from "@benchristel/taste"

class Calculator {}

test("a Calculator", {
  "initially displays 0"() {
    const calculator = new Calculator()
    expect(calculator.display(), is, "0")
  },
})</code></pre><p>The typechecker still isn&#8217;t happy, because there&#8217;s no display() method. Let&#8217;s add that.</p><pre><code>import {test, expect, is} from "@benchristel/taste"

class Calculator {
  display() {}
}

test("a Calculator", {
  "initially displays 0"() {
    const calculator = new Calculator()
    expect(calculator.display(), is, "0")
  },
})</code></pre><p>And now, both the parser and the typechecker are happy. Step complete!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZyL3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZyL3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 424w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 848w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 1272w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZyL3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png" width="570" height="158.94230769230768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:406,&quot;width&quot;:1456,&quot;resizeWidth&quot;:570,&quot;bytes&quot;:86539,&quot;alt&quot;:&quot;Two rows of squares, the bottom one labeled syntax and the top one labeled typechecking. The syntax row has been extended to the right and the typechecking row contains three squares: green, red, green, with an arrow arcing between the two green squares.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two rows of squares, the bottom one labeled syntax and the top one labeled typechecking. The syntax row has been extended to the right and the typechecking row contains three squares: green, red, green, with an arrow arcing between the two green squares." title="Two rows of squares, the bottom one labeled syntax and the top one labeled typechecking. The syntax row has been extended to the right and the typechecking row contains three squares: green, red, green, with an arrow arcing between the two green squares." srcset="https://substackcdn.com/image/fetch/$s_!ZyL3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 424w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 848w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 1272w, https://substackcdn.com/image/fetch/$s_!ZyL3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80348b20-c2d7-434f-b6ee-f5c11a06b546_1698x474.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#8230;except, the test fails.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xTjG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xTjG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 424w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 848w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 1272w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xTjG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png" width="468" height="203.78571428571428" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:634,&quot;width&quot;:1456,&quot;resizeWidth&quot;:468,&quot;bytes&quot;:113620,&quot;alt&quot;:&quot;The same as the previous image, but there is a third row labeled Tests. It has two squares, green on the left and red on the right. An arrow points up and to the right from the green square and ends in midair over the red square.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The same as the previous image, but there is a third row labeled Tests. It has two squares, green on the left and red on the right. An arrow points up and to the right from the green square and ends in midair over the red square." title="The same as the previous image, but there is a third row labeled Tests. It has two squares, green on the left and red on the right. An arrow points up and to the right from the green square and ends in midair over the red square." srcset="https://substackcdn.com/image/fetch/$s_!xTjG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 424w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 848w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 1272w, https://substackcdn.com/image/fetch/$s_!xTjG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb069da58-ca4b-4235-8324-9616391f4ae3_1698x739.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Getting the tests to pass is pretty easy: <code>return &#8220;0&#8221;</code>. I&#8217;m getting kind of tired of drawing these pictures, but here&#8217;s a final view of our journey from green to green.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_iUu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_iUu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 424w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 848w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 1272w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_iUu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png" width="1456" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:187146,&quot;alt&quot;:&quot;The Tests row now has three squares: green, red, green. An arrow arcs between the two green squares.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://bensguide.substack.com/i/166494927?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The Tests row now has three squares: green, red, green. An arrow arcs between the two green squares." title="The Tests row now has three squares: green, red, green. An arrow arcs between the two green squares." srcset="https://substackcdn.com/image/fetch/$s_!_iUu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 424w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 848w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 1272w, https://substackcdn.com/image/fetch/$s_!_iUu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51990d8-66d9-4c14-8d8e-89ff080ec1b7_2858x741.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Debrief</h2><p>I think you get the idea. We take steps at multiple <a href="https://benchristel.com/posts/alexandrian-software/01-levels-of-scale.html">levels of scale</a>, using multiple layers of verification that tell us how &#8220;ready&#8221; we are to ship software.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> The smallest scale, and the most primitive type of verification, is syntax checking, and at that level we can flicker back and forth between ready and unready many times per minute. The next smallest scale (in static languages) is typechecking or compilation. It takes a bit longer to go from green to green at that level. Then come tests, and perhaps linting, and beyond that commit-readiness, integration-readiness, and release-readiness. <strong>We&#8217;re not &#8220;ready ready&#8221; until every level is green.</strong></p><h2>The point</h2><p>The smaller we make our steps, the smoother and safer our work becomes. Smooth and safe are prerequisites for fast. <strong>This is true at all levels of scale.</strong></p><p>When we&#8217;re halfway through a step, in the unready state, we&#8217;re a lot less maneuverable. Changing course is risky because the unfinished work is likely to get in our way and trip us up. In the ready state, we have a chance to get our bearings and figure out which direction to go next. If we don&#8217;t get back to ready frequently, it&#8217;s easy to get lost.</p><p>We&#8217;ve all experienced what happens when we get too far from &#8220;ready,&#8221; e.g. when we spend half an hour typing without ever stopping to compile. When we finally decide to run the code, we get: Syntax error. Syntax error. Type error. Syntax error. Type error. Oh, look, all the tests fail. In the best case, we spend a lot of time debugging. In the worst case, we decide the mess is too big to clean up, and we throw away our changes and start over.</p><p><em>Note: starting over isn&#8217;t always a bad thing; sometimes we &#8220;plan to throw one away&#8221; (as Fred Brooks put it) to learn more about the problem we&#8217;re solving. But wandering away from &#8220;ready&#8221; and getting lost in the weeds isn&#8217;t a strategic sacrifice; it&#8217;s just a blunder.</em></p><p>A similar problem can happen at the larger scales of software development. If you spend too long working on a feature without getting feedback from potential users, you&#8217;re liable to build something they don&#8217;t even want.</p><p>Keep your steps small, at <em>every</em> level of scale, and you&#8217;ll be a happier, more productive programmer.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><a href="https://www.geepawhill.org/2021/10/26/mmmss-a-closer-look-at-steps/">GeePaw calls these levels &#8220;operational contexts.&#8221;</a></p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[A Taxonomy of Testing]]></title><description><![CDATA[Unit vs. system, manual vs. automated]]></description><link>https://bensguide.substack.com/p/a-taxonomy-of-testing</link><guid isPermaLink="false">https://bensguide.substack.com/p/a-taxonomy-of-testing</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 05 May 2025 12:02:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! You&#8217;re reading <em>Ben&#8217;s Guide to Software Development</em>, and I&#8217;m your host, Ben Christel.</p><p>My last few posts have been about the patterns and techniques I use when setting up a new software project:</p><ul><li><p><a href="https://bensguide.substack.com/p/walking-skeleton">Walking Skeleton</a></p></li><li><p><a href="https://bensguide.substack.com/p/entrypoint-document">Entrypoint Document</a></p></li><li><p><a href="https://bensguide.substack.com/p/readme-driven-development">Readme-Driven Development</a></p></li><li><p><a href="https://bensguide.substack.com/p/diamond-design-part-1">Diamond Design, Part 1</a> and <a href="https://bensguide.substack.com/p/diamond-design-part-2">Part 2</a>.</p></li></ul><p>Continuing the theme, I want to write about how to start <strong>testing</strong> a project. The summary is, I tend to start with <strong>manual system testing</strong> of a <a href="https://bensguide.substack.com/p/walking-skeleton">walking skeleton</a>. As the software begins to develop real functionality, I&#8217;ll scaffold each new class and function with <strong>unit tests</strong>. Once the project has hit a self-sustaining rhythm, I&#8217;ll <strong>automate</strong> some of the <strong>system testing</strong>, but put almost all my weight on <strong>unit tests</strong>.</p><p>In order to make it fully clear what all of that means, I&#8217;m going to have to provide some definitions. That&#8217;s what this post is all about!</p><h2>What is a unit test?</h2><p>&#8220;Unit test&#8221; has got to be one of the most maligned, misunderstood, and variously defined terms in our industry. <a href="https://blogs.vmware.com/tanzu/what-is-a-unit-test-the-answer-might-surprise-you/">Matt K. Parker has written a bit about the term&#8217;s origins</a> in the Smalltalk test framework SUnit:</p><blockquote><p>One of the primary goals of SUnit was that each test could run in isolation from all of the other tests. In other words, a &#8220;unit test&#8221; is a test that&#8217;s isolated from all of the other tests in the test suite. The &#8220;unit&#8221; is the test itself!</p></blockquote><p>&#8220;Isolated&#8221; means a test&#8217;s setup should not depend on other tests having been run before it. Each test should start from a clean slate and do its own setup. Nor should a test interfere with other tests that might run afterward. Tests should be good citizens and clean up after themselves (or not make a mess to begin with).</p><p>I think test isolation is a great idea! But I have a problem with equating &#8220;unit test&#8221; with &#8220;isolated test.&#8221; Really, <em><strong>all</strong></em><strong> tests benefit from isolation</strong>. The world has moved on since SUnit, and these days, test isolation goes without saying. In a modern software dev shop, the original definition of &#8220;unit test&#8221; is so all-encompassing as to be useless.</p><p>Moreover, while I think originalist definitions like this one can be illuminating, they&#8217;re not the end of the story. &#8220;Unit test&#8221; has a colloquial meaning, which has diverged considerably from the original definition. That&#8217;s the definition I want to talk about today.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>Here it is:</p><div class="pullquote"><p><strong>A unit test runs in the same <a href="https://bensguide.substack.com/p/processes">OS process</a> as the code being tested, and calls it directly.</strong></p></div><p>One thing I like about this definition is how simple and unambiguous it is. Is your test file importing production functions and classes and doing mad science on them? If so, it&#8217;s a unit test.</p><p>I know some people are going to disagree with me on this one. In particular, you&#8217;ll notice that my definition fails to distinguish between <a href="https://blog.thecodewhisperer.com/permalink/integrated-tests-are-a-scam">integrated tests</a> and unit tests. To save you time, <a href="https://github.com/benchristel/benchristel.github.io/wiki/UnitTest">here are my counterarguments to the other defintions of &#8220;unit test&#8221; I&#8217;ve seen</a>. Please direct your annoyance toward the comments section!</p><h2>System tests</h2><p>If your test file <em>doesn&#8217;t</em> import production code, it&#8217;s not a unit test. So what is it? A <strong>system test</strong>.</p><p>System tests, as the name suggests, interact with a whole (installed or deployed) software <em>system</em>. They simulate actions a user might take: clicking on things and entering data (in the case of a website or GUI app), running commands (in the case of a CLI) or making API calls (in the case of a web service). Other names for system tests include &#8220;end-to-end tests&#8221; and &#8220;integration (or integrated) tests,&#8221; though the latter term is ambiguous.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p>One neat thing about system tests is that, because they interact with the system through an externally-visible interface, they don&#8217;t have to be written in the same language as the production code. There are many system testing frameworks and domain-specific languages available, especially for GUI testing. One I&#8217;ve used recently is <a href="https://www.cypress.io/">Cypress</a>; I&#8217;m also interested in <a href="https://playwright.dev/">Playwright</a>. For system testing CLIs, Samir Talwar has created <a href="https://github.com/SamirTalwar/smoke">Smoke</a>.</p><p>What about system-testing a library? At first glance, it might seem like libraries only need unit tests. After all, their clients are going to be calling them in-process, so in-process unit tests should be perfectly adequate. However, there is also the question of testing the library&#8217;s installer, packaging, or configuration. For that, you&#8217;ll want some system tests that create a new project (e.g. via <code>npm init</code> in NodeJS), install the library in it, and confirm that the library can be used.</p><p>System testing tends to involve more complex infrastructure than unit testing. One of the first problems you&#8217;ll run into is that you need an automated way to compile a complete, usable build of your software (though not necessarily a production build) and put it somewhere another program can get at it. Often, this means installing it in some kind of sandboxed local environment specific to the test run. &#8220;Sandboxing&#8221; means the installation shares nothing with other installations. That way, different runs of the tests don&#8217;t interfere with each other (see the discussion on test isolation above), and you know exactly what code you&#8217;re testing.</p><p>Setting up the infrastructure to do this is an investment that will pay dividends. The ability to create sandboxed installations of your software is valuable even if you&#8217;re not doing automated system testing. For example, you&#8217;ll often want to have a dev build of your software installed alongside the &#8220;real&#8221; version that you use day-to-day (you <em>do</em> use your own software, right?) and sandboxed installations make that easy. Setting up a project with automated system tests thus makes manual testing easier too.</p><p>Speaking of which&#8230;</p><h2>Manual system testing</h2><p>So far, I&#8217;ve been writing as if all tests are automated. But manual testing exists too, and in many cases, its benefits outweigh its costs.</p><p>One advantage of manual testing is that it lacks the main <em>disadvantages</em> of automated system tests, which tend to be <strong>flaky</strong> and <strong>brittle</strong>. &#8220;Flaky&#8221; means they fail intermittently, in ways that appear nondeterministic. &#8220;Brittle&#8221; means they&#8217;re prone to failing when you make some innocuous change to the code, like swapping the order of two buttons. There are ways to write system tests that minimize these problems, but you can&#8217;t completely solve them. By contrast, manual testing is more resilient to change and disturbance in the system.</p><p>A second downside of automated system tests is that, while they can catch functional problems, they can&#8217;t alert you when your UI fails to be understandable, usable, or attractive. The presence or absence of these hard-to-quantify attributes can only be discovered by a human who is actually trying to use the software.</p><p>For all these reasons, I prefer to do most of my GUI testing manually. In future posts, I&#8217;ll talk about the techniques I use to make this feasible. For now, here&#8217;s the gist:</p><ul><li><p>I write UI code in an MVC (model-view-controller) style &#8212; separating the logic and state of the UI (the model) from how it&#8217;s presented to users (the view).</p></li><li><p>Because the model knows nothing about the DOM or the UI framework I&#8217;m using, it&#8217;s easy to unit test. That&#8217;s good, because the model contains all the complicated, test-worthy logic.</p></li><li><p>The view code is extremely simple &#8212; &#8220;too simple to break.&#8221; It&#8217;s usually little more than an HTML template with slots for data, and doesn&#8217;t need unit tests.</p></li><li><p>I manually test the UI whenever I make changes to the view.</p></li><li><p>I sometimes use image-based snapshot testing to catch unintended visual changes. <a href="https://www.chromatic.com/">Chromatic</a> is a service that will do this for you.</p></li></ul><h2>Manual unit testing?</h2><p>So far, we&#8217;ve covered automated unit tests, automated system tests, and manual system tests. Are there manual unit tests? How would that even work?</p><p>Perhaps REPL-driven development or &#8220;live programming&#8221; could be viewed as a type of manual unit testing. In this style of development, you write code in an interactive prompt (a REPL or read-eval-print loop), calling each new function to check that it&#8217;s doing what you expect.</p><p>I don&#8217;t have much experience with REPL-driven development. It appears to be most popular among Lisp and Clojure programmers. If you know something about it, I&#8217;d love to hear your recommendations for where I could learn more!</p><h2>Wrap-up</h2><p>We&#8217;ve talked about two dimensions along which tests vary:</p><ul><li><p>Manual vs. automated</p></li><li><p>System vs. unit</p></li></ul><p>A <strong>unit test</strong> is any test that runs in the same OS process as the code it&#8217;s testing, and calls that code directly. A <strong>system test</strong> interacts with a whole software system the way a user or client would.</p><p>When I&#8217;m starting a new project, I begin with <strong>manual system testing</strong>. The software&#8217;s tiny and cute, so it starts up fast, and there&#8217;s not much to test. As the software grows, I add <strong>unit tests</strong> for much of the new code. Referring back to my posts on <a href="https://bensguide.substack.com/p/diamond-design-part-1">Diamond Design</a>, I will unit test all code in these facets:</p><ul><li><p><code>domain/</code></p></li><li><p><code>platform/</code></p></li><li><p><code>lib/</code></p></li></ul><p>I often skip unit testing in the fourth facet, <code>app/</code>. Instead, I keep app/ as simple as possible, preferring to push complexity into the other facets and under the microscope lens of unit tests. When I make changes to app/, I manually test them through the UI.</p><p>Sooner or later, all that manual testing starts to feel repetitive. At that point, I automate the drudgery with system tests. However, I&#8217;ll continue manual testing throughout the project, looking for visual bugs and user experience issues.</p><p>Overall, I have found that this approach to testing is easy to get started, scales well to large projects (&gt; 1 million lines) and, in conjuction with the other techniques I describe in this newsletter, is capable of producing nearly bug-free software. If you&#8217;d like to hear more about how I write code, feel free to subscribe!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://bensguide.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://bensguide.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>This definition of &#8220;unit test&#8221; is designed to be inclusive of all the ways the term is used colloquially. I came up with the definition by applying the principle that if my friends and colleagues refer to something as a unit test, it had better be included. I think I did an okay job at this, but feel free to let me know if you disagree!</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>An &#8220;integration test&#8221; aims to verify that the major architectural components of the software (e.g. GUI, backend, and database) work together, and doesn&#8217;t try to test functionality in detail. The term &#8220;integrat<em>ed</em> test,&#8221; on the other hand, means different things depending on who you talk to. Some people say that a unit test that invokes multiple production classes is an integrated test (yes, it&#8217;s almost always classes, not functions. Functional programmers don&#8217;t worry about this kind of thing). Others reserve the term for system tests. It&#8217;s very confusing, so I avoid the term &#8220;integrated test&#8221; entirely.</p></div></div>]]></content:encoded></item><item><title><![CDATA[How I make decisions]]></title><description><![CDATA[Here&#8217;s how I actually make decisions when programming.]]></description><link>https://bensguide.substack.com/p/how-i-make-decisions</link><guid isPermaLink="false">https://bensguide.substack.com/p/how-i-make-decisions</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 03 Feb 2025 12:00:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/FKTxC9pl-WM" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here&#8217;s how I actually make decisions when programming. Given two options, A and B:</p><ul><li><p>I briefly imagine the effects of the first option. What will happen if I do A? What will future me, future them, future us experience? I consider the good effects as well as the bad, trying to conceptualize them <em>as a whole</em>. What will it be like to live in that world?</p></li><li><p>Then, in the same way, I imagine the effects of the second option. What will the world be like if I do B?</p></li><li><p><strong>Then I pick the option whose effects I&#8217;d rather live with.</strong></p></li></ul><p>That&#8217;s it!</p><h2>Ben, this is just pros and cons lists. It&#8217;s like, the oldest decision-making trick in the book.</h2><p>Yes, but. There&#8217;s some additional nuance here.</p><p>First of all, I almost never write down the pros and cons. There are a few reasons for that.</p><ul><li><p>I use this technique hundreds of times per day &#8212; literally every time I make a decision. I couldn&#8217;t get anything done if I paused to make a list each time.</p></li><li><p>I use this technique for <em>tiny</em> decisions. What should I name this test? How should I phrase this sentence? Should I put a blank line <em>here</em> or <em>there</em>? In what order should I do these 3 operations?</p></li><li><p>The effects of a decision are not certain. Some are likely to occur, some unlikely. Also, magnitudes like cost or effort have a confidence interval. But trying to put a number on probabilities and confidence intervals is a distraction.</p></li><li><p>The effects of a decision are, in general, far too numerous to list.</p></li><li><p>Presupposing that any given effect is a pro or a con is unnecessary and confusing. To know whether something is good or bad, you may have to look at the larger context. That&#8217;s why I avoid focusing on individual effects and instead consider each bundle of effects <em>as a whole</em>.</p></li><li><p>I might not be willing to admit in writing how much certain effects matter to me. &#8220;I want to use <code>pnpm</code> instead of <code>npm</code> as my package manager because I&#8217;m afraid people will make fun of me for using <code>npm</code>.&#8221; That statement probably won&#8217;t appear on my pros/cons list, but it might be a factor in my decision.</p></li></ul><p>I also don&#8217;t think through every effect <em>consciously and</em> <em>intellectually</em>. The way I experience this technique subjectively is that I hold a decision in mind, and then the effects <em>briefly</em> flicker through consciousness, as subconscious mental processes return their predictions. Sometimes I double-check those predictions, but most of the time I just trust my subconscious. It&#8217;s pretty good at what it does. Yes, sometimes the predictions are wrong &#8212; then I admit my mistake and learn from it.</p><h2>It&#8217;s not as easy as it looks</h2><p>The subjective experience I described above is probably something akin to &#8220;mastery.&#8221; One does not simply decide to adopt this process. First, you have to train your subconscious mind to predict the effects of tiny programming decisions, and that takes practice. In particular, it requires experience making decisions and then living with their results.</p><blockquote><p>Good judgment comes from experience, and experience comes from bad judgment.</p><p>(<a href="https://quoteinvestigator.com/2017/02/23/judgment/">variously attributed</a>)</p></blockquote><p>I highly recommend watching Kathy Sierra&#8217;s video about &#8220;Making Badass Developers&#8221; if you want to learn more about how this kind of training works. You <em>can</em> learn to make decisions this way, <em>if</em> you put in the hours of deliberate practice.</p><div id="youtube2-FKTxC9pl-WM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;FKTxC9pl-WM&quot;,&quot;startTime&quot;:&quot;918s&quot;,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/FKTxC9pl-WM?start=918s&amp;rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Needless to say, <strong>you also need both empathy and accountability for this process to produce anything close to globally-optimal decisions</strong>. (Hopefully this is obvious, but I felt the need to stick this disclaimer in somewhere.)</p><h2>Why this post?</h2><p>I decided to write this post because I think some of my past posts (especially the <a href="https://bensguide.substack.com/p/great-code-is-ceviche">CEVICHE</a> one) implied that I have a very rational, goal-directed design process, and that just isn&#8217;t the case.</p><p>I&#8217;ve written briefly about my actual process before, but it was buried in the middle of a <a href="https://benchristel.com/posts/alexandrian-software/the-alexandrian-solution.html">long post</a>. I thought repeating the idea here might help clarify things.</p><blockquote><p><em>This</em> is the need that I constantly return to when creating software. Whenever I am thinking about what to do and how to do it, I always evaluate the result (or my guess at what the result will be) by the life that it creates. By which I mean: is the resulting interaction more pleasant than the status quo? Or, more profoundly: does it make me feel more alive, more of a person, more connected to the work, more truly at ease being myself?</p></blockquote><p></p>]]></content:encoded></item><item><title><![CDATA[Diamond Design, Part 2]]></title><description><![CDATA[Hello and happy new year!]]></description><link>https://bensguide.substack.com/p/diamond-design-part-2</link><guid isPermaLink="false">https://bensguide.substack.com/p/diamond-design-part-2</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 27 Jan 2025 12:03:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello and happy new year! You&#8217;re reading Ben&#8217;s Guide to Software Development. This post is a continuation of the <a href="https://bensguide.substack.com/p/diamond-design-part-1">previous one</a>, describing the codebase organization approach I&#8217;m calling &#8220;diamond design.&#8221;</p><h2>Recap</h2><p>In part 1, we discussed the <code>app/</code> and <code>lib/</code> folders, which form the top and bottom &#8220;layers&#8221; of our program. <code>lib/</code> is at the bottom and can&#8217;t call any of the other layers. <code>app/</code> is at the top and contains the glue and user interface code that holds everything else together.</p><p>In this post, we&#8217;ll cover the stuff in the middle: <strong>domain/</strong> and <strong>platform/</strong>.</p><h3>Errata in Part 1</h3><p>Ignore the stuff I wrote about code organization <em>within</em> subfolders of app/ and lib/. My actual codebases break these &#8220;rules&#8221; all the time, and I&#8217;m not sure how valuable they are even as general guidelines. I&#8217;ll need to think about this topic a bit more.</p><h2>Domain and business logic</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FskW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FskW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!FskW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!FskW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!FskW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FskW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png" width="458" height="386.95985401459853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1096,&quot;resizeWidth&quot;:458,&quot;bytes&quot;:124685,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FskW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!FskW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!FskW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!FskW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef2052d-ccd7-467a-b228-0b14d4532b56_1096x926.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Domain logic is the core of our application. Domain code is arguably the most important code we write, because it defines <strong>the concepts and behavior that users care about</strong>.</p><p>In a music production app, the domain code talks about tracks, notes, instruments, and samples. In a financial planning app, it&#8217;s all about currencies, budgets, interest rates, and stocks. In a language-learning app&#8217;s domain code, you&#8217;ll find vocabulary, lessons, spaced repetition strategies, and practice exercises.</p><p>Domain code is <strong>platform-agnostic</strong>. It doesn&#8217;t know or care where our code runs (a web browser? a smart TV?) or how it presents itself to users (a command line interface? A GUI?) It can get away with that because it just defines <strong>data types</strong> and the <strong>operations</strong> on those types.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> It <em>doesn&#8217;t</em> know about the user interface or about data persistence.</p><p>Some people like to distinguish &#8220;domain logic&#8221; from &#8220;business logic.&#8221; AFAIK, the difference is that domain code concerns itself with what is logically possible, while business code concerns itself with what is allowed by policy. For example, in a social media app, the statement &#8220;A <code>follows</code> relationship is between two <code>accounts</code>, the <code>follower</code> and the <code>followee</code>&#8221; describes the domain, while &#8220;A <code>user</code> can <code>follow</code> no more than 500 <code>accounts</code>&#8221; describes a business rule.</p><p>If we wanted to uphold this distinction in code, the &#8220;follows&#8221; relationship would be implemented in <code>domain/</code>, while the 500-followees limit could be implemented in <code>app/</code> (possibly in a web controller or service layer) or possibly in its own <code>business/</code> layer between <code>app/</code> and <code>domain/</code>. I&#8217;ve never bothered making this distinction in my programs, though, and can&#8217;t say I have a strong (or particularly informed) opinion about it.</p><h2>Platform code</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ifep!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ifep!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!ifep!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!ifep!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!ifep!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ifep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png" width="422" height="356.54379562043795" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1096,&quot;resizeWidth&quot;:422,&quot;bytes&quot;:124697,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ifep!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!ifep!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!ifep!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!ifep!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa3f1c2d-5822-4b34-a8fe-a2c5c6c17895_1096x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Now let&#8217;s switch gears and talk about platform code. Platform code is the polar opposite of domain code. Just as domain code knows nothing about the platform, platform code knows nothing about the domain. That enables it to be reused between applications with different domains.</p><p>I define &#8220;platform code&#8221; as encompassing everything that</p><ul><li><p>makes the program available to users: the UI technology, the installer, the web framework, etc.</p></li><li><p>connects the program to the real world, via <a href="https://bensguide.substack.com/p/effects">effects</a>.</p></li></ul><p>Examples of platform code include:</p><ul><li><p>The event loop for a GUI app or game</p></li><li><p>Routines for reading and writing files</p></li><li><p>A SQL query builder</p></li><li><p>A web server framework</p></li><li><p>A suite of React components implementing your company&#8217;s design system.</p></li></ul><p>Why separate platform code from domain code? One reason is to ensure that your most valuable code &#8212; your domain code &#8212; is <strong>platform independent</strong>. Platform independence gives you amazing flexibility. It&#8217;s why, for instance, <a href="https://typescriptlang.org/play">the TypeScript compiler can run in a web browser</a>, even though it &#8220;normally&#8221; reads code from a filesystem. The part of the code that knows about filesystems is strictly separated from the part that knows about TypeScript syntax and semantics, allowing the second part to run without the first.</p><h2>Summary</h2><p>Here&#8217;s another visual summary of the different types of code in diamond design:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!koBf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!koBf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 424w, https://substackcdn.com/image/fetch/$s_!koBf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 848w, https://substackcdn.com/image/fetch/$s_!koBf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!koBf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!koBf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png" width="1347" height="1018" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1018,&quot;width&quot;:1347,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:114086,&quot;alt&quot;:&quot;A two-by-two matrix. The rows are labeled \&quot;platform-specific\&quot; and \&quot;platform-agnostic\&quot;. The columns are labeled \&quot;domain-specific\&quot; and \&quot;domain-agnostic\&quot;. The \&quot;platform-specific, domain-specific\&quot; cell is labeled \&quot;app\&quot;. The \&quot;platform-specific, domain-agnostic\&quot; cell is labeled \&quot;platform\&quot;. The \&quot;platform-agnostic, domain-specific\&quot; cell is labeled \&quot;domain\&quot;. The \&quot;platform-agnostic, domain-agnostic\&quot; cell is labeled \&quot;lib\&quot;. Stars in the \&quot;app\&quot; and \&quot;platform\&quot; cells indicate that code here has effects. Arrows indicating dependency point from app to each other cell, from domain to lib, and from platform to lib.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A two-by-two matrix. The rows are labeled &quot;platform-specific&quot; and &quot;platform-agnostic&quot;. The columns are labeled &quot;domain-specific&quot; and &quot;domain-agnostic&quot;. The &quot;platform-specific, domain-specific&quot; cell is labeled &quot;app&quot;. The &quot;platform-specific, domain-agnostic&quot; cell is labeled &quot;platform&quot;. The &quot;platform-agnostic, domain-specific&quot; cell is labeled &quot;domain&quot;. The &quot;platform-agnostic, domain-agnostic&quot; cell is labeled &quot;lib&quot;. Stars in the &quot;app&quot; and &quot;platform&quot; cells indicate that code here has effects. Arrows indicating dependency point from app to each other cell, from domain to lib, and from platform to lib." title="A two-by-two matrix. The rows are labeled &quot;platform-specific&quot; and &quot;platform-agnostic&quot;. The columns are labeled &quot;domain-specific&quot; and &quot;domain-agnostic&quot;. The &quot;platform-specific, domain-specific&quot; cell is labeled &quot;app&quot;. The &quot;platform-specific, domain-agnostic&quot; cell is labeled &quot;platform&quot;. The &quot;platform-agnostic, domain-specific&quot; cell is labeled &quot;domain&quot;. The &quot;platform-agnostic, domain-agnostic&quot; cell is labeled &quot;lib&quot;. Stars in the &quot;app&quot; and &quot;platform&quot; cells indicate that code here has effects. Arrows indicating dependency point from app to each other cell, from domain to lib, and from platform to lib." srcset="https://substackcdn.com/image/fetch/$s_!koBf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 424w, https://substackcdn.com/image/fetch/$s_!koBf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 848w, https://substackcdn.com/image/fetch/$s_!koBf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!koBf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47113739-062e-4e99-a1bc-8ba84ff2f0fb_1347x1018.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Why diamond design?</h2><p>There are several reasons you might choose to adopt something like diamond design in your applications. I&#8217;ll briefly touch on a few of them here.</p><h3>Encoding knowledge</h3><p>I have come to see software development as a special case of &#8220;teaching systems how to do things.&#8221; Whenever I learn something or figure out how to do something, I want to put that knowledge into the system &#8212; <em>encode</em> it into the system &#8212; so that the system as a whole will remember how to do the thing I just did even after I&#8217;m gone.</p><p><a href="https://github.com/software-patterns/workshop/blob/master/patterns/test-loving-architecture.md">Elsewhere</a>, I wrote,</p><blockquote><p>To build steadily on our abilities, step by step &#8212; that is the goal of engineering.</p></blockquote><p>The idea of &#8220;building on <em>our</em> (collective) abilities&#8221; implies a shared pool of knowledge from which we can draw. In order for this knowledge to be legible and reusable, it has to be organized. Diamond design provides that organization.</p><p>Suppose, for example, that I figure out a neat way to solve some platform-level problem, e.g. making paginated database queries. If I then encode that knowledge so the platform-level logic is intertwined with application-specific code, I&#8217;ve missed an opportunity. No one else can reuse my code as-is, and it may take some effort to reverse-engineer my discovery. Although I&#8217;ve solved my immediate problem, I&#8217;ve failed to expand the system&#8217;s capability to solve future problems.</p><p>If, on the other hand, I carefully separate the different types of code, each part becomes easier to understand and reuse. The system is then better able to adapt to new needs.</p><h3>Work styles</h3><p>Each type of code in Diamond Design benefits from different techniques, different views, and even different attitudes. Separating them gives programmers a clearer sense of how to behave around each type of code.</p><ul><li><p>App code tends to be &#8220;move fast and break things.&#8221; Nothing else depends on it, so it&#8217;s free to change. Lib code, by contrast, needs to be &#8220;slow and solid,&#8221; because it&#8217;s the foundation for everything else.</p></li><li><p>Platform and app code can be hard to unit-test due to their effects, so they must be kept &#8220;too simple to break.&#8221; Type checkers can help seal the testing gap, by verifying that app code wires its dependencies together correctly.</p></li><li><p>Domain code, on the other hand, can and should be thoroughly tested. It might also benefit from review by nontechnical folks. <a href="https://www.youtube.com/watch?v=MlPQ0FsPxPY">To hear Scott Wlaschin tell it</a>, domain experts are often perfectly capable of pointing out bugs in a set of F# type definitions.</p></li></ul><h2>Bonus facet: formats/</h2><p>The four-way distinction between app, domain, platform and lib code makes for pretty pictures, but in practice I have found myself adding a fifth category, for code that deals with serialized (i.e. stringified or binary) data representations which are persisted to disk or sent between processes.</p><p>Here&#8217;s how <code>formats/</code> fits into the picture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XRL6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XRL6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 424w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 848w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 1272w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XRL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png" width="468" height="456.8978102189781" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1070,&quot;width&quot;:1096,&quot;resizeWidth&quot;:468,&quot;bytes&quot;:150721,&quot;alt&quot;:&quot;app code depends on format code, which depends on domain code. App code can also call domain code directly.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="app code depends on format code, which depends on domain code. App code can also call domain code directly." title="app code depends on format code, which depends on domain code. App code can also call domain code directly." srcset="https://substackcdn.com/image/fetch/$s_!XRL6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 424w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 848w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 1272w, https://substackcdn.com/image/fetch/$s_!XRL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe5ef7222-ae0c-4f7c-98c1-792b63daa37e_1096x1070.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Code in <code>formats/</code> consists of pure functions, without effects. These functions are of two types: parsers and formatters. Parsers take the serialized representation and convert it to the in-memory <code>domain/</code> model. Formatters go the other way.</p><p>&#8220;Why not just use <code>JSON.parse()</code>?&#8221; you might be thinking. And you have a point. When a program is new, there&#8217;s little motivation to create bespoke parsers and formatters. You just use whatever facilities your language has built in to convert your domain objects to and from a serialized format and that&#8217;s that.</p><p>But what happens when the domain model changes, e.g. to add a new field to an object? When you parse data created by old versions of your program, that field will be <code>null</code>. So you&#8217;ll have to change your code to handle nulls.</p><p>It&#8217;s easy enough to give a missing field a default value, but you want to have a central place for the logic. Otherwise, it will get duplicated all over your codebase and create a mess. The <code>formats/</code> facet is that central place. As your data format accumulates more changes, you&#8217;ll probably end up wanting a versioning system, and the ability to migrate old versions of the format to the latest. <code>formats/</code> is the place to deal with all that.</p><p>The overall purpose of <code>formats/</code> is to shield your domain model from the grimy details of persistence, and ensure that it does not need to battle the ghosts of its past selves in the form of legacy data. You want your domain code clean and pristine, and <code>formats/</code> allows it to be so.</p><p>A common mistake with <code>formats/</code> is to make the domain code depend on the formatting code instead of the other way around. That compromises the purity of the domain code, though, and complicates things if you ever have <em>multiple</em> formats you need to write. So it&#8217;s better to arrange things as in the diagram above.</p><p>You probably won&#8217;t need <code>formats/</code> if all your data is in a relational database you can run migrations on. If it&#8217;s stored in files, though, or a NoSQL database, you&#8217;ll need parsers and formatters sooner or later.</p><h2>What am I missing?</h2><p>As I mentioned, Diamond Design is only a half-baked idea, and I&#8217;d love to hear your feedback and suggestions. How do you organize your code? Let me know in the comments (on substack) or in reply to this email (if you&#8217;re reading this as an email).</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Further reading: <em><a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional/">Domain Modeling Made Functional</a></em><a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional/"> by Scott Wlaschin.</a></p></div></div>]]></content:encoded></item><item><title><![CDATA[Diamond Design, Part 1]]></title><description><![CDATA[An alternative to the dreaded `utils` folder]]></description><link>https://bensguide.substack.com/p/diamond-design-part-1</link><guid isPermaLink="false">https://bensguide.substack.com/p/diamond-design-part-1</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Thu, 26 Dec 2024 12:04:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3uuu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When your codebase is smaller than a few hundred lines, everything can go in one file. But as the code gets more complicated, you&#8217;ll probably want to split it into multiple files, so that:</p><ul><li><p>You can more easily see how the larger <a href="https://benchristel.com/posts/alexandrian-software/alexander.html#centers">centers</a> relate (e.g. through imports) and how centers in the same file cohere.</p></li><li><p>You don&#8217;t get distracted by extraneous code as you&#8217;re programming.</p></li></ul><p>Once you start creating multiple files, though, you have to answer some hard questions. What should the files be called? How should they be organized?</p><p>In this post, I want to talk about my current &#8220;Platonic ideal&#8221; of how to organize application source code, which for the purposes of this post I&#8217;ll call <strong>diamond design</strong>. Diamond design organizes code into four &#8220;facets:&#8221;</p><ul><li><p>Product-specific code</p></li><li><p>Domain and business logic</p></li><li><p>Platform code</p></li><li><p>Library code</p></li></ul><p>In this post, we&#8217;ll cover <strong>product-specific</strong> and <strong>library code</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3uuu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3uuu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 424w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 848w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 1272w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3uuu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png" width="554" height="540.8576642335767" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1070,&quot;width&quot;:1096,&quot;resizeWidth&quot;:554,&quot;bytes&quot;:142349,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3uuu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 424w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 848w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 1272w, https://substackcdn.com/image/fetch/$s_!3uuu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ddac9-2012-45ff-92cd-e6a521ac4ffb_1096x1070.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>&#8220;Diamond design&#8221; is a work in progress</h2><p>The ideas in this post are likely to undergo revision over the next few years. One sign: while I&#8217;ve tried to employ this organization scheme on several projects, I haven&#8217;t adhered to it exactly on any of them. Maybe the concept is fundamentally flawed, or maybe reality is just too messy for any rigid organization scheme to work perfectly. In any case, use these ideas with caution, adapt them to your context, and expect change.</p><p>I&#8217;m publishing this post anyway, in the hope of clarifying my own thoughts and getting feedback. If you figure out a variant of diamond design that works better, I&#8217;d love to hear about it!</p><h2>Product-specific code</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LBpx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LBpx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LBpx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png" width="401" height="338.80109489051097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1096,&quot;resizeWidth&quot;:401,&quot;bytes&quot;:122290,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LBpx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!LBpx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76117461-eb84-4aa5-a1d1-f62103cf269e_1096x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Product-specific</em> means the code knows about both&#8230;</p><ul><li><p>the <em>platform</em>, i.e. how your product is delivered to users (Is it a web app? A command line tool? A mobile game? A GraphQL API? A browser extension?)</p></li><li><p>the <em>domain</em>, i.e. what the product is &#8220;about.&#8221; (Music production? Graphic design? Finance? Dungeons and Dragons?)</p></li></ul><p>Product-specific code is the glue that holds these other facets together. It generally includes the entrypoint (<code>main()</code>), any application frameworks, installers, or other scaffolding, the user interface, and possibly other stuff.</p><p>Product-specific code depends on and connects every other part of your program. Because it depends on so many things, it is constantly changing.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> If not managed carefully, these dependencies make it complex, hard to reason about, and hard to test.</p><p>In spite of these downsides, <strong>the great majority of application code is product-specific.</strong> That&#8217;s probably inevitable to some extent &#8212; gluing a domain to a user interface just seems to require a lot of words somehow. Still, &#8220;big&#8221; is not the same thing as &#8220;complicated,&#8221; and it&#8217;s complexity that really hurts. <strong>The purpose of diamond design is to reduce the </strong><em><strong>complexity</strong></em><strong> of product-specific code as much as possible,</strong> by moving logic into the other facets<strong>. </strong>The hypothesis (supported by my experience) is that this will result in a system that is easier to maintain and improve.</p><h3>Naming</h3><p>I typically put product-specific code in a folder called <code>app/</code>. Not the most creative, I know. But it gets the job done.</p><h3>Organizing product-specific code</h3><p>Since product-specific code tends to be bulky, it is likely to need some amount of internal organization. We can apply a simple principle here:</p><ul><li><p>Things that change together should live together.</p></li><li><p>Things that change at different times, at different rates, or for different reasons should live apart.</p></li></ul><p>I remember hearing this advice from <a href="https://sandimetz.com/">Sandi Metz</a>, though I don&#8217;t have the exact source handy. The reason we want things that change together to live together is simple: it makes it easier to find the code you have to change. Plus, if you want to cleanly <em>remove</em> a feature (something I suspect software companies will be doing a lot of in a few years) it&#8217;s much easier to do if all the code is in one folder.</p><p>This is an argument <em>against</em> the Ruby on Rails approach of e.g. having separate folders for models, controllers, and views. You&#8217;re rarely going to change all of your models at the same time, or all your controllers at the same time. But almost every feature you build will touch a model, a controller, and a view.</p><p>Therefore, if you want changes to cluster, it&#8217;s better to have a separate folder per feature, or per page. You might, for example, have a bunch of code related to logging in and out, resetting passwords, and so on. That can go in <code>login/</code>. The code for user settings can go in <code>settings/</code>. The code for discussion posts can go in <code>discussions/</code>. Et cetera.</p><p>What about code that&#8217;s used by multiple features? Well, maybe you need an <code>app/shared/</code> directory&#8230; but quite possibly that code shouldn&#8217;t go in <code>app/</code> at all. Remember, there are three other facets of the diamond we haven&#8217;t even talked about yet! So let&#8217;s look at those alternatives.</p><h2>Library code</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uo20!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uo20!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!uo20!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!uo20!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!uo20!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uo20!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png" width="398" height="336.2664233576642" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1096,&quot;resizeWidth&quot;:398,&quot;bytes&quot;:121485,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uo20!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 424w, https://substackcdn.com/image/fetch/$s_!uo20!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 848w, https://substackcdn.com/image/fetch/$s_!uo20!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 1272w, https://substackcdn.com/image/fetch/$s_!uo20!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06d3bb35-e2ed-47ca-9d9d-5d8d0efabb37_1096x926.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can go to the opposite extreme from app-specific code and write code that is coupled to neither our domain nor our platform. That&#8217;s <strong>library code</strong>. Library functions often deal with language-level primitives (e.g. strings and arrays) or open standards (e.g. JSON).</p><p>Elephant in the room: why can&#8217;t we use a third-party library? Why can&#8217;t we use the standard library that comes with the programming language?</p><p>If you can get the code you need that way, go ahead and do it. But:</p><ul><li><p><strong>If you&#8217;re shopping for third-party libraries:</strong> beware of supply-chain attacks. Choose libraries with few or no dependencies of their own. Choose something that&#8217;s been around long enough to have a track record. Check that the maintainer&#8217;s incentives align reasonably well with yours. Installing a library is a bigger commitment than most people realize, and an NPM package that someone wrote in a weekend and abandoned is probably not something you want to marry your application to.</p></li><li><p><strong>If you&#8217;re using the standard library:</strong> consider whether your application code could express itself more eloquently in terms of a simpler abstraction or a differently-shaped API. Standard libraries often prioritize exposing low-level functionality over ergonomics &#8212; sometimes for performance reasons, sometimes because the general problem really is that complicated.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> You know your needs better, and can build a library that&#8217;s just right for you.</p></li></ul><p>When neither the standard library nor a third-party library will fit your needs, you need a place to put your own internal library code. This facet gives you a place.</p><p>Here&#8217;s an example of when you might write library code: in JavaScript, there&#8217;s no built-in function to find all occurrences of one string within another. Weird, right? It seems like that would be a pretty commonly-used function. If you want it, though, you&#8217;ll have to write it yourself.</p><pre><code>// I, Ben Christel, am the author of this code block.
// I hereby release it into the public domain.

const NOT_FOUND_INDEX = -1

/**
 * Finds each occurrence of `needle` in `haystack`.
 *
 * @yields each index within `haystack` at which an occurrence of
 * `needle` starts. Occurrences can overlap. Indices are yielded in
 * ascending order.
 */
function *indicesOf(needle, haystack) {
  for (let i = 0; i &lt;= haystack.length; i++) {
    i = haystack.indexOf(needle, i)
    if (i === NOT_FOUND_INDEX) {
      break
    }
    yield i
  }
}</code></pre><p>When you write functions like this, you should put them in an easily-findable place separate from the rest of your code. The reason is simple: someday, someone else is going to want that indicesOf function you just wrote, and if they can&#8217;t find it they&#8217;re going to implement it all over again. If, on the other hand, they can easily find it by browsing <code>src/lib/strings.js</code>, they&#8217;ll be delighted. Even if the function they want turns out to be slightly different from the one you wrote (maybe they need <code>nonOverlappingIndicesOf</code> or <code>reverseIndicesOf</code>), seeing your code will ease the task of writing their own.</p><p>A word of caution: &#8220;library code&#8221; is not equivalent to the notorious <code>utils/</code> folder. When I say &#8220;library code,&#8221; I&#8217;m using that as shorthand for code that&#8230;</p><ul><li><p>is domain- and platform-agnostic</p></li><li><p>manipulates language primitives (strings, arrays), open standards (URLs, XML, JSON Schema), or other ubiquitous concepts (e.g. dates and times, or geospatial data)</p></li></ul><p><strong>Here&#8217;s the litmus test for deciding whether something is library code.</strong> When you look at <em>just that code</em> in isolation, can you tell what application it&#8217;s from? Can you infer anything about the domain, or the kind of program it is, whether it&#8217;s a web service or a Minecraft mod or what? If you can, it ain&#8217;t library code. It goes in one of the other facets &#8212; probably <code>domain/</code> or <code>platform/</code>.</p><h3>Naming</h3><p>I generally put library code in <code>src/lib/</code>. That name is confusing, though, if the project is itself a library (e.g. an NPM package) so I&#8217;ve sometimes used <code>general-purpose/</code> or <code>gp/</code> instead.</p><h3>Organizing library code</h3><p>I generally put library functions in a file named after the most structured type on which the function operates. For example, these functions operate on both numbers and strings:</p><pre><code>function parseNumber(s: string): number | undefined

function formatNumber(x: number): string</code></pre><p>Since numbers are &#8220;more structured&#8221; than strings, in the sense that some (but not all) strings represent numbers, but all numbers can be written as strings, the functions would live in <code>src/lib/numbers.ts</code>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p>Organizing library code that deals with open standards is easy: the file is named after the standard. Code that deals with URLs goes in <code>src/lib/urls.ts</code>.</p><h2>Up next: platform and domain code</h2><p>In the next post, I&#8217;ll talk about the other two facets of diamond design: platform code and domain code. Stay tuned.</p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>This follows from the <a href="https://bensguide.substack.com/i/139993670/a-definition-of-dependency">definition of dependency</a> in one of my previous posts.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This is why JavaScript&#8217;s <code>Array#sort</code> method sorts in-place rather than returning a sorted copy of the array. Sorting a copy is more often what you want, but if the standard lib made the copy for you, there would be no way to recover the more efficient in-place sort from that API.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>To get a sense of what &#8220;more structured&#8221; means, see Alexis King&#8217;s post &#8220;<a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/">Parse, don&#8217;t validate</a>.&#8221;</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[The Zero-Bug Rules]]></title><description><![CDATA[There once was an Old West town called Two Dudes.]]></description><link>https://bensguide.substack.com/p/the-zero-bug-rules</link><guid isPermaLink="false">https://bensguide.substack.com/p/the-zero-bug-rules</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 16 Dec 2024 12:02:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_OUw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There once was an Old West town called Two Dudes. There were only two buildings in Two Dudes &#8212; a jail and a saloon. The town&#8217;s eponymous pair of inhabitants included the town sheriff, who also kept bar at the saloon by day (or did he do that at night? One never can be sure with these old yarns) and the saloon&#8217;s sole patron, a man by the name of Bad Ed. Bad Ed was a notorious thief, and once he&#8217;d downed a few drinks he&#8217;d usually round out the night with a crime spree. Fortunately for the town, the sheriff knew exactly where to find Bad Ed, and always had him behind bars by the next morning.</p><p>(Bad Ed never spent more than a few hours in jail, of course, because how was the sheriff going to make a living if the saloon didn&#8217;t have any customers? Er, customer.)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_OUw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_OUw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 424w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 848w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 1272w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_OUw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp" width="1024" height="683" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:683,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two historical interpreters, standing in a gravel road outside a wooden building with a sign reading \&quot;Six-Gun Saloon\&quot; and shooting at each other with pistols.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two historical interpreters, standing in a gravel road outside a wooden building with a sign reading &quot;Six-Gun Saloon&quot; and shooting at each other with pistols." title="Two historical interpreters, standing in a gravel road outside a wooden building with a sign reading &quot;Six-Gun Saloon&quot; and shooting at each other with pistols." srcset="https://substackcdn.com/image/fetch/$s_!_OUw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 424w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 848w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 1272w, https://substackcdn.com/image/fetch/$s_!_OUw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9584d114-7270-4f0b-9d3b-c4f89c56d892_1024x683.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">"<a href="https://www.rawpixel.com/image/582630/carol-highsmiths-texas-photograph">Two 'gunslingers' (historical intrepreters Wes</a>" by Carol M Highsmith is marked with <a href="https://creativecommons.org/publicdomain/zero/1.0/?ref=openverse">CC0 1.0</a>.</figcaption></figure></div><div><hr></div><p>Much like detecting crime in Two Dudes, finding bugs in a two-line codebase is easy. Debugging only becomes hard when the guilty line of code is hiding among a thousand innocent ones, and you don&#8217;t know where it&#8217;s hiding when you start investigating. You have to investigate (and reject) many more incorrect hypotheses, before you find the correct one.</p><p>This might be why programmers, on average, spend about 50% of their time debugging.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> That&#8217;s a lot! My own experience indicates that this percentage can probably be reduced significantly, and that programming becomes a lot more fun when you don&#8217;t have to debug as much.</p><p>If only all our codebases could be like Two Dudes, we&#8217;d never need a debugger. We&#8217;d never need to write another <code>print</code> or <code>console.log</code>. Of course, life can&#8217;t be that simple.</p><p>Or can it?</p><h2>A zero-bug development process</h2><p>If you had some way of spotting a bug <em>immediately after writing it</em>, you wouldn&#8217;t need a debugger. You&#8217;d <em>know</em> which line was guilty: <strong>it&#8217;s the line of code you just wrote</strong>. So you&#8217;d just hit ctrl+Z, undo your last change, and the bug would be gone. </p><p>It&#8217;s actually possible to work like this, as long as you:</p><ul><li><p>Maintain a <strong>complete suite of tests</strong> (or other automated <a href="https://bensguide.substack.com/i/150330330/verifiable">verification</a> checks) at all times.</p></li><li><p><strong><a href="https://naildrivin5.com/blog/2022/09/06/actual-reasons-to-use-tdd.html">Derive your tests from your intentions</a></strong> for what the code should do, not from what it happens to do currently. Then, <strong>make the code match the tests</strong>. Not the other way around.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p></li><li><p><strong>Work in very small steps.</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> Keep the intervals in which your code will not compile or run very short, ideally on the order of a few seconds. <a href="https://benchristel.github.io/process-to-processes/ChangingOneFunction/PairDelimiters.html">Here is a tiny technique that helps with this</a>.</p></li><li><p><strong>Run your tests continuously</strong> &#8212; every time your code reaches a compilable state.</p></li></ul><p>Following these &#8220;zero-bug rules&#8221; ensures that you will notice most bugs as soon as you introduce them, and can revert them immediately. As a bonus, you&#8217;ll understand your code so well that any bugs that do slip through to production will be easy to diagnose. Your reaction to a bug report will be &#8220;d&#8217;oh! I should have thought of that!&#8221; instead of &#8220;that shouldn&#8217;t be possible!&#8221;</p><h2>Why achieving zero bugs is hard</h2><p>For many software developers, the big stumbling block is the &#8220;run your tests continuously&#8221; rule. You can&#8217;t follow this rule if your test suite takes minutes to run. You can&#8217;t even really do it if it takes 10 seconds to run. You need to aim for the <a href="https://lawsofux.com/doherty-threshold/">Doherty threshold</a>: feedback in 400 milliseconds or less. In future posts, I&#8217;ll talk about how to design your code so it&#8217;s possible for tests to run this quickly. For now, I&#8217;ll just assert that <a href="https://github.com/benchristel/taste/blob/e42ab12dc75fdb23eb44419e419af3f28710fdf0/README.md#fast">it&#8217;s possible</a>.</p><p>One development process you can use to implement the zero-bug rules is <strong>test-driven development</strong>. I happen to like TDD a lot, but maybe you don&#8217;t. Fortunately for you, there are probably other possible implementations of the zero-bug rules. Maybe you&#8217;ll invent the next great one.</p><h2>Further Reading</h2><ul><li><p><a href="https://craftbettersoftware.com/p/debugging-the-biggest-waste">Debugging: the biggest waste</a> by Daniel Moka</p></li></ul><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Alaboudi, Abdulaziz and LaToza, Thomas D., 2021. &#8220;<a href="https://www.researchgate.net/publication/351354680_An_Exploratory_Study_of_Debugging_Episodes">An Exploratory Study of Debugging Episodes</a>.&#8221;</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This criterion could probably be included as part of the definition of a &#8220;complete&#8221; suite of tests. If a test doesn&#8217;t describe intentional behavior, it can&#8217;t contribute meaningful coverage (because when it fails, you don&#8217;t know if the code is wrong, or the test is wrong).</p><p>Sometimes, the purpose of tests is simply to preserve the current behavior of the software. This is true, e.g. for &#8220;characterization tests&#8221; written to pin down legacy code for refactoring. In this case, you <em>should</em> derive tests from the code, because the goal is &#8220;bug-for-bug compatibility.&#8221; However, you should <em>not</em> be characterization-testing newly written code; you should be doing test-driven development. If newly written code has bugs, you don&#8217;t want to bake them in with tests.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><a href="https://www.geepawhill.org/2021/10/26/mmmss-a-closer-look-at-steps/">GeePaw Hill defines a &#8220;step&#8221;</a> as &#8220;a purposeful gap of unreadiness between two points of readiness on some system&#8217;s timeline, a before point and an after point.&#8221;</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Readme-driven development]]></title><description><![CDATA[My first forays into programming, in middle school and high school, were in the domain of computer games &#8212; written in Visual Basic, of all things.]]></description><link>https://bensguide.substack.com/p/readme-driven-development</link><guid isPermaLink="false">https://bensguide.substack.com/p/readme-driven-development</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Sat, 14 Dec 2024 12:02:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!pOnz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pOnz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pOnz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pOnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg" width="562" height="421.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1024,&quot;resizeWidth&quot;:562,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;meeting doodle&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="meeting doodle" title="meeting doodle" srcset="https://substackcdn.com/image/fetch/$s_!pOnz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!pOnz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2dce5664-fe4f-49ab-8b39-b7b95d2b0ae0_1024x768.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">"<a href="https://www.flickr.com/photos/55408947@N00/6115989801">meeting doodle</a>" by <a href="https://www.flickr.com/photos/55408947@N00">healthserviceglasses</a> is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/2.0/?ref=openverse">CC BY-NC-SA 2.0</a>.</figcaption></figure></div><p>My first forays into programming, in middle school and high school, were in the domain of computer games &#8212; written in Visual Basic, of all things. I had limited access to the family PC, so I spent a lot of time away from the computer daydreaming about the next feature I would build for this or that game.</p><p>I recorded my ideas in the form of user manuals, even going so far as to draw &#8220;screenshots&#8221; of what I imagined the UI would look like. This seemed natural to me, because at the time, a lot of games <em>did</em> have lavish, illustrated user manuals. Two of my favorites were the manuals for <a href="https://ia600500.us.archive.org/12/items/sc2000_manual/SimCity%202000%20-%20Manual_text.pdf">Sim City 2000</a> and <a href="https://retrogamer.biz/wp-content/uploads/2015/12/FA-18_Korea_-_Manual_-_PC.pdf">F/A-18 Korea</a>. These were the touchstones that I tried to emulate as I wrote detailed tutorials and reference documents for imaginary games &#8212; many of which I never actually built.</p><p>I&#8217;m sharing these memoirs in the hope of convincing you that <strong>writing documentation can be fun</strong>. In fact, if you&#8217;re not careful, it can turn into a guilty pleasure. Writing documentation for programs that don&#8217;t exist yet gives you a feeling of accomplishment for less effort than it would take to write working code. But it&#8217;s also useful: documentation helps you plan the implementation.</p><p>Your pre-implementation docs don&#8217;t have to be elaborate. In fact, it&#8217;s better if they&#8217;re not, because your plans will almost certainly change. That&#8217;s why the technique we&#8217;re discussing today is called &#8220;readme-driven development&#8221; and not &#8220;documentation-driven development.&#8221; A readme is the minimum viable documentation to get your project off the ground.</p><blockquote><p>Plans are worthless, but planning is everything.</p><p>&#8212;<a href="https://quoteinvestigator.com/2017/11/18/planning/">Dwight D. Eisenhower</a></p></blockquote><h2>How to begin</h2><p><code>README.md</code> is often the first file I create in a new project. Before I have any code, I write a bit of prose about what exactly I&#8217;m going to build.</p><p>What goes in a readme varies by project, but I usually start with:</p><ul><li><p>An explanation of <strong>what the project is and why I&#8217;m doing it</strong>. What problem does it solve? What&#8217;s unique about it? What will make it stand out from the competition? (Technique: <a href="https://benchristel.github.io/process-to-processes/Fundamentals/SayWhy.html">Say Why</a>)</p></li><li><p>A sketch of a user manual. How do you install the thing? What&#8217;s its public interface?</p></li><li><p>High-level thoughts about implementation, e.g. data formats and architecture.</p></li><li><p>Developer documentation. (See: <a href="https://benchristel.github.io/process-to-processes/StartingAProject/WalkingSkeleton.html">Walking Skeleton</a>)</p></li></ul><h2>Why</h2><p>Starting with a readme&#8230;</p><ul><li><p>helps your team agree on what you all are going to do before you do it.</p></li><li><p>provides the basis for a backlog of user stories.</p></li><li><p>reveals flaws in the design early, when they&#8217;re easy to fix.</p></li><li><p>records your ideas &#8212;&nbsp;they may be valuable later, even if you don&#8217;t end up implementing them in the first version.</p></li><li><p>grounds your designs and keeps you from accidentally straying from your original vision. (You can, of course, <em>intentionally change</em> your vision later.)</p></li><li><p>ensures you write documentation.</p></li></ul><h2>Pitfalls</h2><p>Don&#8217;t fall into the trap of gold-plating your readme. Remember that it&#8217;s supposed to be concise and low-polish. Avoid getting too committed to your early plans &#8212; they <em>will</em> change.</p><p>Another possible failure mode is that you make plans that are so ambitious they turn out to be unimplementable. The readme-writing phase of a project is a good time to dream big, but perhaps not too big. Remember, <em>you</em> are going to have to build this thing. So make grand plans, by all means, but hold them very loosely. If you share the readme with people outside your immediate team, be sure to communicate that it is <em>aspirational</em> &#8212; a highly speculative rough draft. Put prominent notices to that effect in the readme itself.</p><p>As soon as you run into implementation difficulties, consider whether a different design would be more feasible, or whether you can ditch the feature entirely. My motto is: <strong>never hesitate to cut scope</strong>. You can always add it back later.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><h2>Examples</h2><p>Here are some first drafts of readmes I&#8217;ve written for my projects:</p><ul><li><p><a href="https://github.com/benchristel/mdsite/blob/96bd2a45f19fa82de42bd25b910964ebbf2385d0/README.md">mdsite</a></p></li><li><p><a href="https://github.com/benchristel/audition/blob/c3bfaaa692af164fd47efa46595c8640d43f3d11/README.md">audition</a></p></li><li><p><a href="https://github.com/benchristel/marss/blob/4a11057c9058851e04219f2e46a98ee93b84ef6e/README.md">marss</a></p></li></ul><p>It&#8217;s fun (for me) to look back at these old readmes and see just how much has changed about the design of the programs they&#8217;re supposed to document. Half the features described in the original <code>mdsite</code> and <code>audition</code> readmes either got scrapped or were implemented differently. Even so, writing these readmes was extremely helpful for clarifying my thoughts and planning implementation.</p><p>In a few of my projects, writing the docs was the fun and valuable part, so I stopped there:</p><ul><li><p><a href="https://github.com/benchristel/verse-format/blob/606140350f3f18a248ab39a4a7463de48980dcb1/README.md">The Verse Format</a></p></li><li><p><a href="https://github.com/benchristel/odd/blob/ac1075955e23d4c595d3c5d213dc88993d1e8c14/README.md">odd</a></p></li></ul><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>One of my favorite examples of a successful project that had to cut scope is the game <em><a href="https://en.wikipedia.org/wiki/Heroes_of_Might_and_Magic_IV">Heroes of Might and Magic IV</a></em>. Rushed to production as the studio was running out of money, it is the flawed-yet-beautiful end result of a series of thoughtful compromises.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Entrypoint Document]]></title><description><![CDATA[How to find what's important]]></description><link>https://bensguide.substack.com/p/entrypoint-document</link><guid isPermaLink="false">https://bensguide.substack.com/p/entrypoint-document</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 02 Dec 2024 12:01:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!erXk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!erXk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!erXk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!erXk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!erXk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!erXk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!erXk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg" width="394" height="519.9175257731958" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:776,&quot;resizeWidth&quot;:394,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;An Asian-style gate in a stone wall&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="An Asian-style gate in a stone wall" title="An Asian-style gate in a stone wall" srcset="https://substackcdn.com/image/fetch/$s_!erXk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!erXk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!erXk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!erXk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29188c2-7986-4fe2-8f5a-0856f5cba527_776x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">"<a href="https://www.flickr.com/photos/9751325@N02/2881097357">A Gate of 'Beautiful and Young'</a>" by <a href="https://www.flickr.com/photos/9751325@N02">kudumomo</a> is licensed under <a href="https://creativecommons.org/licenses/by/2.0/?ref=openverse">CC BY 2.0</a>.</figcaption></figure></div><p><strong>Situation:</strong> you&#8217;re writing documentation that you hope will help someone a year or more down the road.</p><p><strong>Problem:</strong> you don&#8217;t know how to ensure this hypothetical future person will find the document you wrote.</p><p><strong>Solution:</strong> create an <strong>entrypoint document</strong> for each major <a href="https://benchristel.com/posts/alexandrian-software/alexander.html#centers">center</a> (codebase, project, team, product, company) in your environment. The entrypoint document should consist of a short list of links to other, topical documentation. Put it in a prominent place and direct new teammates to it. That way, people will be able to find the docs they need, and you will have a framework on which to hang new documentation.</p><p>What a &#8220;prominent place&#8221; means depends on the type of center you&#8217;re documenting. If you&#8217;re writing an entrypoint for&#8230;</p><ul><li><p>an <strong>in-person team</strong>: consider hanging up a poster in the team&#8217;s workspace with a minified link.</p></li><li><p>a <strong>distributed team</strong>: pin a post in the team&#8217;s chatroom or main communication channel. <a href="https://en.wikipedia.org/wiki/Slack_(software)">Slack</a> now associates a &#8220;canvas&#8221; &#8212; basically a collaborative text document &#8212; with each channel, which you can use for this purpose.</p></li><li><p>a <strong>codebase</strong>: README.md in the repository root directory.</p></li><li><p>a <em><strong>part</strong></em><strong> of a codebase</strong>: maybe a README.md in the relevant directory, though this can be hit-or-miss as far as finding it later goes. I wish IDEs drew your attention to this type of documentation as you browsed the code.</p></li><li><p>a <strong>project</strong>: wherever collaboration on that project primarily happens. This might be your issue tracker, chatroom, office space, discussion forum, or GitHub repo.</p></li><li><p>a <strong>product</strong> or <strong>company</strong>: an internal website (or maybe even <a href="https://labspractices.com/">a public one</a>!) that everyone visits during new hire orientation.</p></li></ul><p>A general rule: keep the documentation close to the thing it&#8217;s describing. Put it somewhere people visit often. A common antipattern is to document <em>code</em> in a separate <a href="https://en.wikipedia.org/wiki/Content_management_system">CMS</a>. This is bad because it separates the documentation from the thing being documented, making the docs harder to find and less likely to be updated.</p><p>A possible failure mode is that the entrypoint document gets way too big for anyone to comprehend. Aim for <a href="https://labs.la.utexas.edu/gilden/files/2016/04/MagicNumberSeven-Miller1956.pdf">seven links on the page, plus or minus two</a>. Keep &#8216;em high-level. Example:</p><pre><code># Welcome to &lt;project name&gt;!

- Emergency contact in case of a production issue: &lt;link&gt;
- Main chatroom (ask for help here): &lt;link&gt;
- Onboarding guide: &lt;link&gt;
- UI designs: &lt;link&gt;
- Issue tracker: &lt;link&gt;
- Codebase: &lt;link&gt;
- Staging server: &lt;link&gt;</code></pre><p>When you create new documents, link to them <em>somewhere</em> in the tree rooted at the entrypoint doc, but don&#8217;t add them to the entrypoint itself. Keep the hierarchy shallow. If you have seven links on each page, then 3 levels of hierarchy have room for over 300 pages. If you need more pages than that, it&#8217;s better to increase the branching factor than to increase the tree depth.</p><p>Another possible failure mode: you end up with <em>several</em> entrypoint documents, with team-, project-, and code-centric links all jumbled together, and no clear source of truth. The fix is to clearly delineate what each entrypoint is for, and move links where they belong. Each entrypoint page will probably still link to the others; that&#8217;s fine.</p><p>No matter what you do, there is a chance that future generations won&#8217;t even be able to find your entrypoint document. There&#8217;s no technocratic solution to this. The ultimate source of cultural continuity is <em>always</em> oral tradition upheld by people and their interactions. So keep talking to each other.</p><h2>Further reading</h2><ul><li><p>The &#8220;<a href="https://benchristel.com/posts/alexandrian-software/02-strong-centers.html">strong centers</a>&#8221; property of living structure, from Christopher Alexander.</p></li></ul><h2>Examples</h2><ul><li><p><a href="https://labspractices.com/">Pivotal Labs practices</a> (a company-wide entrypoint)</p></li><li><p>Arguably, my <a href="https://benchristel.com/portal/">web portal</a> page is the entrypoint document for my life. It gets away with violating the 7 +/- 2 rule because a) I am the only user, b) it has search, and c) I update it daily, so it stays fresh in my mind.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Walking Skeleton]]></title><description><![CDATA[How to begin]]></description><link>https://bensguide.substack.com/p/walking-skeleton</link><guid isPermaLink="false">https://bensguide.substack.com/p/walking-skeleton</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Tue, 26 Nov 2024 13:03:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4GBi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p>When I told Tillie that six steps seemed a lot to have to do before you begin, she said, &#8220;You must think of those six steps not as preparation for the beginning but as the beginning itself.&#8221;</p><p>&#8212;E.L. Konigsburg, <em>The View from Saturday</em></p></blockquote><p>It&#8217;s Day One, you&#8217;re the first engineer on the project, and you don&#8217;t have any code yet. What do you do?</p><p>Much of your time is probably going to be taken up learning about the business, the product, and the vision for the user experience. You might be interviewing prospective coworkers. You might be working with the product manager to assemble a backlog of user stories.</p><p>But when it comes to writing the first line of code, where do you start?</p><p>I like to start with a <strong>walking skeleton.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4GBi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4GBi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4GBi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg" width="317" height="475.2679355783309" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:683,&quot;resizeWidth&quot;:317,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;IMG_0022 - Manny Calavera&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="IMG_0022 - Manny Calavera" title="IMG_0022 - Manny Calavera" srcset="https://substackcdn.com/image/fetch/$s_!4GBi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4GBi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F016db7df-c474-4ab8-84ad-9541fbc76e1b_683x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">"<a href="https://www.flickr.com/photos/92161664@N00/7088953655">IMG_0022 - Manny Calavera</a>" by <a href="https://www.flickr.com/photos/92161664@N00">Anime Nut</a> is licensed under <a href="https://creativecommons.org/licenses/by-nc-nd/2.0/?ref=openverse">CC BY-NC-ND 2.0</a>.</figcaption></figure></div><h2>The Walking Skeleton pattern</h2><p>A walking skeleton is&#8230;</p><ul><li><p>a <strong>deployable codebase</strong></p></li><li><p>that implements a single, <strong>trivial behavior</strong> (e.g. displaying &#8220;hello world&#8221;)</p></li><li><p>but nevertheless integrates together all the <strong>major architectural components</strong> that you foresee in your real application (client, server, database, etc.)</p></li></ul><p>The term &#8220;Walking Skeleton&#8221; was coined by Alistair Cockburn in the &#8216;90s, and <a href="http://web.archive.org/web/20170214035145/http://alistair.cockburn.us/Walking+skeleton">written up on his website in 2008</a>. Here&#8217;s how he defines the concept:</p><blockquote><p>A Walking Skeleton is a tiny implementation of the system that performs a small end-to-end function. It need not use the final architecture, but it should link together the main architectural components. The architecture and the functionality can then evolve in parallel.</p></blockquote><p>To this definition, I&#8217;d add a couple criteria:</p><ul><li><p>A walking skeleton should be <strong>built simply and quickly</strong>. Cockburn says it generally takes 20 minutes to 2 weeks. That sounds about right to me. You can do it even quicker with off-the-shelf project templates, but I prefer not to use those, for reasons I&#8217;ll explain in a bit.</p></li><li><p>It should also have a full complement of <strong>tools</strong>: package manager, test suite, static analyzer, formatter, build system, etc.</p></li></ul><h2>Why</h2><p>There are a few different reasons you might want a walking skeleton.</p><p>First, it <strong>boosts morale</strong> and <strong>sets the standard</strong> for the rest of the project. Imagine you&#8217;re starting on a new team. Would you rather find a codebase that&#8217;s tidy and well-appointed? Or a haphazard free-for-all of random hacks and one-off scripts, where everything kinda-sorta-not-really works? A walking skeleton ensures from the start that you will be on the happy path, where the code works and your tools are ready to hand.</p><p>Second, a walking skeleton<strong> minimizes the number of detours developers will have to take</strong> as they start to write features. If a developer feels inclined to write a unit test, you don&#8217;t want any obstacles in their way. The absence of a test framework is a pretty major obstacle. Having a test framework right from the get-go maximizes the chance that tests will get written.</p><p>Third, a walking skeleton guarantees that <strong>you will be able to ship software</strong>. This sounds like a trivial thing, but it&#8217;s amazing how many projects never produce working software at all: the developers write a bunch of code, but it can&#8217;t be integrated or packaged into anything that will run on anyone else&#8217;s computer. The way to prevent that failure mode is to deploy your software on day one, and keep it deployable as it grows.</p><h2>What goes into a walking skeleton?</h2><p>Now I&#8217;m going to venture outside the agile software canon and talk about how <em>I personally</em> like to build walking skeletons.</p><h3>Developer Documentation</h3><p>My first step is generally to write a README file that tells developers how to obtain a copy of the source code, start working on it, and verify their changes.</p><p>(I&#8217;m realizing now that I&#8217;ve skipped over a technique that&#8217;s arguably more foundational than Walking Skeleton: <strong>readme-driven development</strong>. I guess that will be the next post.)</p><p>Since a readme is often addressed to users as well as developers, I usually make a separate &#8220;Development&#8221; section at the end. Sometimes I even hide it, as I did in <a href="https://github.com/benchristel/marss?tab=readme-ov-file#development">the README for my RSS feed generator, marss</a>. Open source projects often have a separate CONTRIBUTING file. You could follow that pattern, too, though IMO it makes the instructions slightly harder to find.</p><p>The most important part of the developer readme is a list of the shell commands that all devs will need to run as they work. These commands should be <strong>simple</strong>: one or two words. They should not require arguments or other customization. This simplicity ensures that everyone working on the software is actually using the same set of tools, not partially-overlapping sets of similar tools. <strong>Consistent tools make consistent results.</strong></p><p>To run your tools, you can use <code>make</code>, or simple shell scripts (e.g. <code>./test</code>) or a language-specific task runner like <code>pnpm</code> or <code>rake</code>. Just be consistent.</p><p>When tools are simple, documentation can be simple. Here&#8217;s an example of the docs I like to write and read:</p><blockquote><pre><code>yarn         # install dependencies; run one-time setup
yarn ts      # typecheck in watch mode
yarn serve   # start dev server
yarn test    # run unit tests (fast)
yarn sys     # run system tests (slow)
yarn lint    # find defects
yarn fix     # fix formatting
yarn verify  # run all checks (do this before you git push)
yarn release # compile, verify, and release a new version</code></pre></blockquote><p>In the spirit of readme-driven development, I usually write the dev docs before I even install anything. The documentation functions as a to-do list of the tools I need to set up. Of course I&#8217;ll amend and augment the list later, if I need to.</p><h3>Tools</h3><p>After you write the readme, the next natural step might be to start building the tools you just documented. But you&#8217;ll quickly find that you need some material to test your tools on: e.g. a unit test that asserts true == true, or some compilable production code. What I generally do is build the &#8220;hello world&#8221; app and its tools together: a bit of app code, a bit of tooling code, in alternating steps.</p><p>As you set up the tools, any configuration files you create should be committed to the source code repository. You might even want to commit editor configuration, though that can get contentious.</p><h3>Deployable &#8220;hello world&#8221; program</h3><p>What exactly does the walking skeleton of a program do? It depends on what it wants to be when it grows up.</p><p>If it&#8217;s going to be a game: you can pop up a window and paint it black.</p><p>If a library:</p><pre><code>export function hello() {
  return "Hello, world!"
}</code></pre><p>If a client-only web app: display &#8220;Hello, world!&#8221; on the page using your UI framework of choice.</p><p>If a command-line tool: <code>console.log(&#8220;Hello, world!&#8221;)</code></p><p>If a full-stack app, you&#8217;ll need a client, a server, a database, and some dummy data (perhaps inserted via a migration) that the client fetches from the server and displays. Consider the <a href="https://12factor.net/">twelve factors</a>. (Yeah, this one is an order of magnitude more work than the others. There&#8217;s a reason people like <a href="https://rubyonrails.org/">Rails</a> and <a href="https://www.phoenixframework.org/">Phoenix</a>.)</p><h3>Putting it all together</h3><p>The overall goal is to end up in a situation where:</p><ul><li><p>All the tools are built and documented.</p></li><li><p>All your verification checks pass.</p></li><li><p>You have deployed a &#8220;hello world&#8221; app to a production-like environment, using the deployment process you documented.</p></li><li><p>Your tooling can remain unchanged as source code is added to the project (see: <a href="https://matklad.github.io/2023/12/31/O(1)-build-file.html">O(1) Build File</a>).</p></li></ul><h2>But Ben, this is so much work. Why not just use an off-the-shelf project generator?</h2><p>So far I&#8217;ve been describing this process like you&#8217;re going to write every line of code in your walking skeleton from scratch. But there are plenty of project generators (e.g. <a href="https://www.npmjs.com/package/create-react-app">create-react-app</a>) that will set everything up for you with a single command. Why not just use those?</p><p>Well, maybe you should. If you&#8217;re on the clock, and you&#8217;re using a programming language in which you&#8217;ve never built a walking skeleton from scratch, don&#8217;t start now. Look for an existing project template and use that. You&#8217;ll find out where the rough edges are soon enough. Maybe you&#8217;ll improve on it for your next project.</p><p>The problem with off-the-shelf templates is that they&#8217;re never going to be exactly suited to your needs. They tend to include a lot of <em>stuff</em>, some of which you will use, some of which you won&#8217;t, and some of which you will tolerate while wishing it were configured differently. Interactions between tools mean that <strong>every tool in the codebase increases the difficulty of adding more</strong>. Extra tools are thus a burden you will have to carry for the lifetime of the project. You could try to remove the tools you aren&#8217;t using, but <strong>it&#8217;s harder to rip out stuff you don&#8217;t want than to add stuff you do</strong>.</p><p>So I strongly recommend working your way up to building walking skeletons by hand, professionally. <a href="https://benchristel.github.io/process-to-processes/SoftwareDevelopment/PersonalProjects.html">Start with your own personal projects</a>. It&#8217;s good practice, and will deepen your familiarity with the programming language and its tooling. <strong>Do it six times</strong> (another technique I need to write about) and you&#8217;ll be an expert. Then you can safely try it at work.</p><p>You might well ask, &#8220;what&#8217;s the point? The off-the-shelf templates are good enough.&#8221; But there&#8217;s a huge amount of confidence to be gained from knowing the purpose of every single line of code in your project&#8217;s infrastructure. Or to put it another way, there&#8217;s a huge amount of confidence to be <em>lost</em> when on day one of your project, you&#8217;re already responsible for maintaining code you didn&#8217;t write and don&#8217;t fully understand.</p><p>Try both options. I bet you&#8217;ll feel the difference.</p><h2>Further Reading</h2><ul><li><p><a href="https://matklad.github.io/2024/03/22/basic-things.html">Alex Kladov&#8217;s &#8220;Basic Things&#8221;</a></p></li><li><p>Examples of walking skeletons I&#8217;ve built recently:</p><ul><li><p><a href="https://github.com/benchristel/1001/commit/419efb0016dd69897bd1565c5431a3f0a8fa075d">Example 1</a></p></li><li><p><a href="https://github.com/benchristel/marss/compare/fdc8dacc0ab754d975fe3a3c4a788c0bd8e00149...18564db5030b583d1ded3e7da9f9c24a8b2c1525">Example 2</a></p></li></ul></li></ul>]]></content:encoded></item><item><title><![CDATA[Great code is CEVICHE]]></title><description><![CDATA[The title of my work-in-progress book is Process to Processes. That&#8217;s because software development starts with a human process (team structure and development workflow) and ends with computer processes that effect some desired behavior. The first process fully determines the nature of the second.]]></description><link>https://bensguide.substack.com/p/great-code-is-ceviche</link><guid isPermaLink="false">https://bensguide.substack.com/p/great-code-is-ceviche</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 18 Nov 2024 12:00:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-Ffc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The title of my work-in-progress book is <em>Process to Processes</em>. That&#8217;s because software development starts with a human process (team structure and development workflow) and ends with computer processes that effect some desired <a href="https://bensguide.substack.com/p/behavior">behavior</a>. The first process fully determines the nature of the second.</p><p>Somewhere in between, we have code. There are lots of ways we can structure the code for any given software process. Many, many (infinitely many?) different structures can create identical behavior. <strong>Yet the thesis of software design, as a discipline, is that some of these structures are &#8220;better&#8221; than others</strong>, even though <em>behaviorally</em> they are all the same.</p><p>This claim seems to take us into the realm of subjective opinions. Who&#8217;s to say what&#8217;s better? What does &#8220;better structure&#8221; really mean?</p><p>Recall from the <a href="https://bensguide.substack.com/p/why-make-software-better">previous post</a> that our high-level goal is to make software development less stressful and more joyful for everyone involved. And that a lot of the stress in our line of work is ultimately caused by mental models that don&#8217;t match reality. If we could build accurate mental models, software development would be easier and more predictable, and we would suffer fewer mishaps along the way.</p><p>As it turns out, the structure of the code can help or hinder our modeling efforts. For the purpose of <em>Process to Processes</em>, <strong>&#8220;better code&#8221; means code that helps us build mental models of the system.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></strong></p><p>However, that definition is too abstract to be practical. We need more specifics. It would be nice if we could look at any given chunk of code and say specifically what&#8217;s good or bad about it. It would be even nicer if we could turn those evaluations into a to-do list of how to improve the code.</p><p>I think we can get there, but it will probably take a book&#8217;s worth of text and examples to explain how. In this post, we&#8217;ll start our journey with a high level overview.</p><h2>Introducing CEVICHE</h2><p>Software people have invented various acronyms to try to characterize what makes code good. We have (at least) <a href="https://blog.cleancoder.com/uncle-bob/2020/10/18/Solid-Relevance.html">SOLID</a>, <a href="https://dannorth.net/cupid-for-joyful-coding/">CUPID</a>, <a href="https://blog.chriszimmerman.net/2015/12/21/Poodr.html">TRUE</a>, and <a href="https://github.com/benchristel/benchristel.github.io/wiki/Commentary:WhereDoesBadCodeComeFrom#a-better-set-of-criteria">WARMED</a>. Coming up with a coherent acronym seemed like a fun challenge, so I thought I&#8217;d try my hand at it. CEVICHE is the (somewhat raw) result. It&#8217;s a list of the <strong>properties of code that enable developers to build accurate mental models of the system</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Ffc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Ffc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Ffc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg" width="1024" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A dish of ceviche, surrounded by sauces and sides served on scallop shells&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A dish of ceviche, surrounded by sauces and sides served on scallop shells" title="A dish of ceviche, surrounded by sauces and sides served on scallop shells" srcset="https://substackcdn.com/image/fetch/$s_!-Ffc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-Ffc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef820d2-1533-47bf-9e02-be9aca632991_1024x768.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">"<a href="https://www.flickr.com/photos/95786359@N05/8770488190">Ceviche</a>" by <a href="https://www.flickr.com/photos/95786359@N05">T.78UopXx</a> is licensed under <a href="https://creativecommons.org/licenses/by-sa/2.0/?ref=openverse">CC BY-SA 2.0</a>.</figcaption></figure></div><p>Good code is:</p><ul><li><p><strong>Controllable:</strong></p><ul><li><p>You can make the code do what you want.</p></li></ul></li><li><p><strong>Efficient:</strong></p><ul><li><p>It doesn&#8217;t waste your time.</p></li></ul></li><li><p><strong>Verifiable:</strong></p><ul><li><p>It inspires trust (via tests or otherwise).</p></li></ul></li><li><p><strong>Instructive:</strong></p><ul><li><p>It might teach you something.</p></li></ul></li><li><p><strong>Consequential:</strong></p><ul><li><p>It&#8217;s load-bearing.</p></li></ul></li><li><p><strong>Harmonious:</strong></p><ul><li><p>It plays well with the rest of the system.</p></li></ul></li><li><p><strong>Extricable:</strong></p><ul><li><p>It makes sense in isolation. You can confidently use it, repurpose it, and ultimately delete it.</p></li></ul></li></ul><p>CEVICHE is meant to complement the other acronyms (SOLID, CUPID, TRUE, WARMED), not to replace them. Comparing and contrasting them is left as an exercise for the reader.</p><p>Let&#8217;s explore the CEVICHE properties in more detail.</p><h2>Controllable</h2><p>Great software is <strong>controllable</strong>. You can find all the knobs and levers, and you know what&#8217;s going to happen when you pull them. Whether you&#8217;re changing the code, or using existing code, it&#8217;s easy to get the software to do what you want. The result is that <strong>controllable code does what its authors intended it to do.</strong> </p><p>Another C-word that I considered for this spot was <strong>correct</strong>. This seems like an obvious choice. We all want code to be correct, right?</p><p>The question is, though: correct by what standard? Usually, in application development, there is no formal spec for what we&#8217;re doing, and even if there is, it will probably change by the next release. This kind of &#8220;correctness&#8221; isn&#8217;t stable; rather, it&#8217;s based on the whims of users and product managers. The desired state of the software is&#8230;</p><ul><li><p>a moving target</p></li><li><p>not fully knowable (we can&#8217;t see inside our users&#8217; heads to know what they want)</p></li></ul><p>Whatever definition of &#8220;correct&#8221; we set up today is likely to be gone by tomorrow. So instead of asking &#8220;is the code correct,&#8221; let&#8217;s consider a slightly different and more useful question: <strong>will it do what we intended?</strong></p><p>As David Bryant Copeland writes in &#8220;<a href="https://naildrivin5.com/blog/2022/09/06/actual-reasons-to-use-tdd.html">Actual Reasons to Use TDD</a>&#8221;:</p><blockquote><p><strong>We can all agree software should do what we expect.</strong> Set aside &#8220;correctness&#8221; (a meaningless term if I&#8217;ve ever heard one). Don&#8217;t worry about &#8220;working software&#8221;. Instead think about the question on our minds as we write code, the question we had from our first moment of coding, and that we still ask as we do our jobs today: is the software doing what I expect?</p><p>&#8212;<a href="https://naildrivin5.com/blog/2022/09/06/actual-reasons-to-use-tdd.html">David Bryant Copeland</a></p></blockquote><p>If we can keep the software doing what we expect it to do, moment by moment as we program, then we can easily adapt it to whatever definition of &#8220;correct&#8221; our customers throw at us. In order to do that, we have to control the software and keep it controllable as it grows.</p><h2>Efficient</h2><p>I have some qualms about including &#8220;efficient&#8221; on this list, because programmers are already prone to &#8220;optimizing&#8221; all the wrong things in all the wrong ways. So remember Donald Knuth&#8217;s warning:</p><blockquote><p>Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.</p><p>Yet we should not pass up our opportunities in that critical 3% [. . .] but only <em>after</em> that code has been identified. It is often a mistake to make a priori judgments about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail.</p><p>&#8212;Donald E. Knuth, &#8220;<a href="https://dl.acm.org/doi/pdf/10.1145/356635.356640">Structured Programming with go to Statements</a>&#8221;</p></blockquote><p>In spite of my doubts, I am going to advocate for writing fast programs &#8212; for a particular definition of &#8220;fast.&#8221; My motive is simply that I think many programs today are too slow to support an optimal development process. For instance, the popular JavaScript test framework Jest <a href="https://github.com/benchristel/taste?tab=readme-ov-file#recommended-integrations">takes longer to start up than Google Chrome</a>! That&#8217;s way too slow to run as part of a programmer&#8217;s &#8220;inner loop.&#8221;</p><p>Indeed, speed is one of my most common complaints with modern software, and one of the main factors I consider when making &#8220;buy vs. build&#8221; decisions. I&#8217;ve written <a href="https://github.com/benchristel/taste">a test framework</a> and a <a href="https://github.com/benchristel/mdsite">static site generator</a> because the alternatives were either too slow or too cumbersome for me to feel really comfortable using them.</p><p>This isn&#8217;t just my pet peeve. In 1982, two researchers at IBM <a href="https://jlelliotton.blogspot.com/p/the-economic-value-of-rapid-response.html">discovered</a> that shortening computer response times to 0.4 seconds (from the accepted standard of 2 seconds) resulted in a disproportionately large improvement in productivity. When people no longer had to wait for the machine, they typed commands faster and spent much less time collecting their thoughts. The critical threshold of <strong>400 milliseconds</strong> has become known as the <a href="https://lawsofux.com/doherty-threshold/">Doherty threshold</a>, after one of the researchers.</p><p>Another important threshold is <a href="https://stackoverflow.com/a/2547903">80&#8211;100 milliseconds</a>, the &#8220;threshold of noticeability.&#8221; While the Doherty threshold is a good target for &#8220;conversational&#8221; interactions like command line interfaces and web browsing, response times below 80 milliseconds are required for an interaction to feel immediate, like you&#8217;re manipulating a physical object. For instance, clicking a button should give you <em>some</em> kind of feedback within 100 milliseconds &#8212; at the very least, an animation confirming the click. Similarly, characters typed in a text field should appear in under 80 milliseconds. It&#8217;s pretty inexcusable for something so basic to take longer than that.</p><p>If you&#8217;re writing a game or an interface that needs to be animated, there&#8217;s a third threshold at 16 milliseconds&#8212;your budget for rendering one frame.</p><p>In light of these thresholds, I can explain what I mean when I say that programs should be &#8220;efficient.&#8221; <strong>An efficient program is one that responds fast enough to support an optimal user experience. </strong>That means commands should run and webpages should load in 400 milliseconds, GUIs should give you feedback in 100 milliseconds, and animations should run at 60 frames per second.</p><p>How can we hit these thresholds? For the most part, <strong>don&#8217;t worry about optimizing application code</strong>. The performance of a typical application is largely determined by three things (in no particular order):</p><ul><li><p><strong>Feature complexity.</strong> with fewer, simpler features, your code can run faster. Focus on the most valuable features and drop everything else.</p></li><li><p><strong>Third-party libraries.</strong> The inner loops of your program will probably not be in code you wrote yourself. Choose fast libraries.</p></li><li><p><strong>Architecture.</strong></p></li></ul><p>&#8220;Architecture&#8221; includes stuff like:</p><ul><li><p>Is your UX event-driven, request-response, or batch?</p></li><li><p>What programming language are you using?</p></li><li><p>What database are you using? (or: how do you organize data at rest?)</p></li><li><p>When and how does I/O happen? Is it parallel or sequential?</p></li><li><p>How far does data have to travel over the network?</p></li><li><p>Are you making many small web requests or a few large ones?</p></li><li><p>Are you using one thread/process, or many? What is your strategy for inter-thread or inter-process communication?</p></li></ul><p>The &#8220;fast&#8221; answers to these questions are often counterintuitive. For now, the best heuristic I can offer is the KISS principle:</p><div class="pullquote"><p><strong>Keep it simple, silly!</strong></p></div><p><a href="https://benchristel.github.io/yt/#https://www.youtube.com/watch?v=-C_hqWGlvnY">Haskell can be faster than C</a>. Flat files can be faster than a database. <a href="https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-mcsherry.pdf">Single-threading is often faster than multi-threading</a> unless you&#8217;re operating at an astronomical scale. You always want to keep in mind how much data or traffic you <em>really</em> need to handle, and not overbuild. Simple architectures scale farther than you think. Start simple, measure, and optimize the bottlenecks.</p><h2>Verifiable</h2><blockquote><p>Trust, but verify.</p><p>&#8212;<a href="https://en.wikipedia.org/wiki/Trust,_but_verify">Russian proverb</a>, often quoted by Ronald Reagan</p></blockquote><p>A <strong>verifiable</strong> program is one that gives us a reason to trust it. When we think of &#8220;verification&#8221; we often think of &#8220;testing,&#8221; but the concepts are not synonyms. <a href="https://en.wikipedia.org/wiki/Software_verification">Software verification</a> includes testing, but also some other techniques:</p><ul><li><p><strong>Static verification techniques</strong></p><ul><li><p>Typechecking</p></li><li><p>Linting</p></li><li><p>Code review</p></li><li><p>Formal proofs</p></li></ul></li><li><p><strong>Dynamic verification techniques</strong></p><ul><li><p>Manual testing / QA</p></li><li><p>Unit testing</p></li><li><p>Automated system testing</p></li><li><p>Monitoring</p></li><li><p>Runtime assertions</p></li></ul></li></ul><p>You don&#8217;t need to use all of these techniques for every program. But they are all useful. As always, pick and choose the most valuable techniques for your context.</p><p>Together, the production code and the systems that verify it can be considered a self-verifying system. A codebase that contains its own tests, types, linter rules, etc. is self-verifying.</p><p>There&#8217;s more to verifiability than just putting tests, types, and other checks into a system. <strong>The code itself needs to be shaped to make verification easy.</strong> In order to be easy to test, code needs to have a simple, consistent interface, a cohesive purpose, deterministic behavior, and, ideally, no <a href="https://bensguide.substack.com/p/effects">effects</a>. There are also constraints on the shapes of code that can be reliably typechecked, as anyone who has tried to retrofit TypeScript onto a legacy JavaScript codebase can attest.</p><p>Verifiability and controllability go together. A self-verifying system resists attempts to bungle it up, and is thus easier to control. When controllability degrades, verifiability helps us get back on track, letting us refactor without fear of breaking everything. When the code isn&#8217;t doing exactly what we want, we can isolate the problem in a test. That way, we can be sure the problem is fixed and won&#8217;t come back.</p><p>Yet controllability and verifiability are also somewhat independent. While <strong>controllability</strong> is about having reliable knobs and levers on our figurative control panel, <strong>verifiability</strong> is about the gauges and dials. In a self-verifying system, we can get information about the current state and feedback about our actions. A self-verifying system can even teach us how to use it. Don&#8217;t know what a line of code does or whether it&#8217;s needed? Delete it and see what tests fail. Want to see where a function is called? Comment it out and see what compile errors you get. Trying to figure out how a function handles an edge case? Read the tests (or write a new one). With the right techniques, verifiability can be more than a safety net: it can help us feel happy, capable, and confident at every moment while we&#8217;re programming.</p><h2>Instructive</h2><blockquote><p>You know you&#8217;re reading good code when you can&#8217;t tell what problem it&#8217;s solving or how it solves it. Good code makes you feel confused, stupid, and ignorant.</p><p>&#8212;no one ever</p></blockquote><p>According to Ward Cunningham, &#8220;You know you are working on clean code when each routine you read turns out to be pretty much what you expected.&#8221; But what if you&#8217;re a noob like me and you don&#8217;t know what to expect? It would be nice if the code could <em>teach</em> me how it works.</p><p>Much of the code I work with is &#8220;easy to read&#8221; only if I already know what it does and why. But sometimes, I come across unfamiliar code and have an &#8220;aha!&#8221; moment. The code is so lucid, so clear and simple, that it has communicated something new to me and I now understand both the problem and its solution more clearly.</p><p><a href="https://dannorth.net/cupid-for-joyful-coding/">Dan North says that good code is domain-based</a>: it uses the language and concepts of the business domain. It&#8217;s nice when variable and function names are recognizable to a nontechnical team member, but we can go one step further. <strong>Ideally, programmers should be able to learn the business domain by reading the code.</strong> If we need to write and read separate documentation to learn about the domain, we&#8217;ve missed an opportunity for efficiency.</p><p>Code can, of course, communicate in technical domains as well as business ones. Great code teaches new programmers old tricks. I&#8217;ve mentioned this example before, but I&#8217;ll repeat it because I love it so much: this regex for extracting C strings from a file taught me how to think about backslash escape-sequences:</p><pre><code>// A string consists of:
// - a double-quote character
// - any number of "units", where a unit is either:
//   - an escape sequence: a backslash followed by any
//     character
//   - a literal character other than a backslash, quote, or
//     newline
// - a closing double-quote.
const cString = /"(\\.|[^\\"\n])*"/</code></pre><p>Another example, more recently discovered: an algorithm for detecting whether two 2-D line segments intersect. The details of the code are obscure, but the <a href="https://stackoverflow.com/a/24392281">explanation on Stack Overflow</a> makes the high-level approach delightfully clear.</p><pre><code>export const segmentsIntersect = (
    [[a, b], [c, d]]: Segment,
    [[p, q], [r, s]]: Segment,
): boolean =&gt; {
    const determinant = (c - a) * (s - q) - (r - p) * (d - b);
    if (determinant === 0) {
        return false;
    } else {
        const lambda = ((s - q) * (r - a) + (p - r) * (s - b))
            / determinant;
        const gamma = ((b - d) * (r - a) + (c - a) * (s - b))
            / determinant;
        return 0 &lt; lambda &amp;&amp; lambda &lt; 1 &amp;&amp; 0 &lt; gamma &amp;&amp; gamma &lt; 1;
    }
};</code></pre><p>Is it significant that both of these examples have commentary longer than the code itself? Maybe. I don&#8217;t always comment my code, but when I do, I aim for comments like these: paragraphs that give a high-level explanation of what&#8217;s going on. In my experience, these comments are the most empowering for future readers, and the most durable. They&#8217;re also a lot of fun to write.</p><p>Still, if you were to criticize the examples above for being terse to the point of obfuscation, I wouldn&#8217;t disagree. Maybe what you should take away from this section is not just &#8220;Good code is instructive&#8221; but also &#8220;Good instruction comes with code examples.&#8221;</p><h2>Consequential</h2><p>Good code is <strong>consequential</strong>. That is, it <em>does something</em>. There is a reason for it to exist.</p><p>This point may seem too trivial to mention, but I&#8217;ve seen and written a lot of dead, tautological, and otherwise useless code in my career. It&#8217;s easier to write inconsequential code than you might think.</p><p>Inconsequential code can be subtle. For example, in the following Ruby snippet, the check for <code>transactions.empty?</code> is not needed, because <code>transactions.all?</code> returns <code>true</code> when <code>transactions</code> is an empty array.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><pre><code>if transactions.empty? || transactions.all?(&amp;:complete?)
  # ...
end</code></pre><p>Do-nothing checks like this can morph into actual bugs as the code changes. I recently fixed a bug in some web server code that looked very similar to the above.  It went something like this (pseudocode):</p><pre><code>if count(jobs) is 0 {
  return
}
for each job in jobs {
  job.perform()
}
doNextStep()</code></pre><p>You can probably spot the bug: <code>doNextStep</code> was supposed to happen even when there were no jobs, but the early return caused it to be skipped. The fix was simply to delete the unnecessary empty-array check.</p><p>The examples above are simple, but in complex codebases, whole sections of logic can &#8220;die&#8221; (when their outputs are no longer used) and become inconsequential code. These sections of inconsequential code are hard to find, so they&#8217;re rarely removed. In a codebase with low <strong>verifiability</strong>, inconsequential code is a <a href="https://en.wikipedia.org/wiki/G._K._Chesterton#Chesterton's_fence">Chesterton&#8217;s Fence</a>. It can be hard to tell that the code isn&#8217;t actually needed, so deleting it seems risky. The cost of proving the code safe to delete is higher, in the short term, than the cost of keeping it around. So it sticks around.</p><p>When inconsequential code piles up, the codebase dies by a thousand cuts. No single bit of derelict code hurts much, but in aggregate they are a huge impediment to change.</p><h2>Harmonious</h2><p>Good code harmonizes with:</p><ul><li><p>Team norms (code style)</p></li><li><p>Development tools</p></li><li><p>Architecture</p></li></ul><p>Disharmonious code compromises the wholeness or coherence of the system, making it harder to describe and thus harder to reason about.</p><p>The idea of <strong>harmonizing code with development tools</strong> deserves further explanation. There are lots of different tools to read and modify code: editors, scripts, version control systems, etc. Code needs to be designed to work with the tools the team is actually using.</p><p>One of the most common tools people use to work with code is text search, or <code>grep</code>. Code should therefore be structured for searchability. That all but rules out dynamic approaches like this:</p><pre><code>// Bad example: this code isn't greppable.
Database["create_" + entityName](id, attributes)</code></pre><p>If you want to find out where <code>Database.create_user</code> is called, this code makes it really difficult.</p><p>Another simple example of harmonizing code with tools, from <a href="https://benchristel.com/posts/alexandrian-software/15-not-separateness.html">my essay on Christopher Alexander</a>:</p><blockquote><p>I was once on a team writing C++. We had a coding convention that dictated that all member variables should be prefixed with an underscore, like <code>_someName</code>. I suggested that we drop this rule, since our IDEs highlighted member variables in a unique shade of purple that made them easy to spot even without a special prefix. My teammates objected: some contributors to our open-source codebase might be using editors that didn&#8217;t have that feature. The prefix was for them, not for us. What seemed like good fit to me (accommodating our code style to the capabilities of <em>our</em> tools) wasn&#8217;t a good fit in the bigger scheme of things.</p></blockquote><p>Harmony is related to the notion of <strong>conceptual coherence</strong> emphasized by Fred Brooks in <em>The Mythical Man-Month</em>.</p><blockquote><p>I will contend that conceptual integrity is the most important consideration in system design. It is better to have a system omit certain anomalous features and improvements, but to reflect one set of design ideas, than to have one that contains many good but independent and uncoordinated ideas.</p><p>&#8212;Fred Brooks, <em>The Mythical Man-Month</em></p></blockquote><p>Conceptual integrity impacts the development process, because when a design choice is at odds with the wholeness of the system, it tends to lock in other choices and make the code more rigid.</p><p>A thought experiment: say we have a codebase with features A, B, and C. These features are independent, in the sense that we can understand, test, use, and improve each one in relative isolation from the others. We can also remove any feature if we find it&#8217;s no longer valuable.</p><p>Now imagine someone wants to add a disharmonious feature, X. Feature X doesn&#8217;t slot nicely into the design. Instead, it cuts across the functionality of A, B, and C, requiring them all to change in different ways. So after adding X, we have a system A&#8217;, B&#8217;, C&#8217;, X. Now, awkwardly, <em>none </em>of the features can be understood in isolation. You need to understand X to understand A&#8217;, B&#8217;, and C&#8217; in their entirety. Moreover, A&#8217;, B&#8217;, and C&#8217; can no longer be removed, because they are needed to support X.</p><p>With only four features, this might be manageable, but of course real systems are more complex. If you start with a dozen or more harmonious features, and then tack on a few disharmonious ones that lock together the others in various combinations, you&#8217;re well on your way to spaghetti code.</p><p>Unfortunately, I don&#8217;t have a real-world example handy, and so this explanation of why disharmonious changes hurt might be totally bogus (though <em>impressionistically</em>, I&#8217;m pretty sure I&#8217;ve experienced it happening). It&#8217;s just my current hypothesis. An area for future research, I suppose.</p><p>One way to deal with this situation is simply not to build feature X. But that&#8217;s not very satisfying. What if we <em>really need it</em>? Think about what design would accommodate A, B, C, and X easily. Refactor the system to that design first. <em>Then</em> add feature X. Kent Beck said it best:</p><blockquote><p>For each desired change, make the change easy (warning: this may be hard), then make the easy change.</p></blockquote><h2>Extricable</h2><p>One more attribute of great code, and then we&#8217;ll wrap up. Great code is extricable from its context. Because it is extricable, it fits in your head, you can understand it in isolation, and you can reuse it in situations its author never imagined.</p><p>My first full-time programming gig was working on a Ruby on Rails app for an e-commerce site. One of the nice / scary things about Ruby on Rails is, it makes it really easy to run ad-hoc code in production. You just shell into your server, type <code>rails console</code>, and bam, you&#8217;ve got an interactive Ruby interpreter, ready with all your web server code and a connection to your production database. The nice thing about this, compared to running SQL statements directly on your data, is that you have access to all your business logic: validation, <code>after_save</code> hooks, and even whole workflows if you need them. So the risk of getting your data into an inconsistent state is (arguably) less than if you had a SQL prompt.</p><p>On one memorable occasion, there was a bug that got a bunch of customer accounts into a weird state, and it was my job to get them unstuck. I knew there was a Ruby method I could call that would do what I wanted; it was called something like <code>finish_signup</code>. There was just one problem: <code>finish_signup</code> would do a bunch of things that I didn&#8217;t want, as well as the thing that I did want. E.g. it would re-send the welcome emails that all of those customers had already received.</p><p>I eventually got the situation untangled, but it wasn&#8217;t as easy I would have liked. And I narrowly avoided substituting one problem for another. <code>finish_signup</code> was a footgun.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p>My takeaway from that experience was this guideline: <strong>&#8220;write code that you&#8217;d trust in a production incident.&#8221;</strong> When you&#8217;re dealing with an emergency, you don&#8217;t want to have to think about what hidden side effects might be invoked by a piece of code. You want code that&#8217;s predictable and side-effect-free.</p><p>In the years that followed, I came to appreciate how this guideline fits in with  approaches like <a href="https://dzone.com/articles/hexagonal-architecture-what-is-it-and-how-does-it">ports-and-adapters (a.k.a. hexagonal) architecture</a>, &#8220;<a href="https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell">functional core, imperative shell</a>,&#8221; and functional programming more generally. When code is made of pure functions and data is immutable, both code and data are easier to move around.</p><ul><li><p>Data can be sent over the wire or persisted</p></li><li><p>Logic can move from the client to the server or vice versa.</p></li><li><p>Application code can be reused in development tools, like a <a href="https://www.geepawhill.org/2024/10/01/basic-concepts-of-the-making-app/">making app</a> or database migration script.</p></li><li><p>The core of your web app can be extracted to a library and repackaged as a command line tool, desktop app, or mobile app&#8230; or simply distributed as a standalone product.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a></p></li></ul><p>Mobility of logic and data makes it possible to redesign the architecture of your application &#8212; the arrangement of processes and how they communicate, the data storage technology, the web framework you&#8217;re using &#8212; without making any changes to your business logic code. Likewise, it ensures that changes to business logic don&#8217;t cascade into infrastructural code. That&#8217;s good, because <strong>business and architecture are separate concerns</strong>. They change at different rates, at different times, and for different reasons. Having to change one when you change the other makes every change much more expensive.</p><p>The property of extricability, though, isn&#8217;t just about writing functional code and immutable data, nor about separating business concerns from technical ones. It&#8217;s about separation of concerns more generally. <strong>Extricable code is meaningful and comprehensible in isolation. </strong>You can run it without running the entire system, and you can understand it without knowing how it&#8217;s currently used. This property allows you to change code safely, even if you don&#8217;t understand every bit of code in the system.</p><h2>Wrap-up</h2><p>We all want software development to be more predictable and less stressful. To make it so, we need an accurate mental model of the system. The best way I know of to start building that model is to write code with the CEVICHE properties.</p><p>CEVICHE is far from perfect. You&#8217;ve probably already thought of things I&#8217;ve left out. What are they? Are there any properties you disagree with? Let me know in the comments.</p><p>In subsequent posts, we&#8217;ll look at specific techniques that help create the CEVICHE properties.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Some might argue &#8220;no, good code is code that&#8217;s easy to <em>change</em>.&#8221; But all code can be changed, trivially. Just open a file and change it. The question is, <em>what effect did that change have</em>? Our ability to answer that question relies on mental modeling.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This is logically sound; see <a href="https://en.wikipedia.org/wiki/Vacuous_truth">vacuous truth</a>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>That is, a gun to shoot yourself in the foot with.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>It might be more realistic to imagine going the other way, from a CLI (written for yourself and other programmers) to a web app for the general public.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Why make software better?]]></title><description><![CDATA[Reintroducing Process to Processes]]></description><link>https://bensguide.substack.com/p/why-make-software-better</link><guid isPermaLink="false">https://bensguide.substack.com/p/why-make-software-better</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Tue, 12 Nov 2024 12:01:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! It&#8217;s been a while. I&#8217;m Ben Christel, and you&#8217;re reading Ben&#8217;s Guide to Software Development.</p><p>I haven&#8217;t worked on my book, <em><a href="https://benchristel.github.io/process-to-processes/">Process to Processes</a></em>, for a couple months. I lost steam because it just felt <em>pointless</em>. I kept asking myself &#8220;who is going to pay (in time or dollars) for this? <a href="https://triggerstrategy.substack.com/p/what-the-hecks-goin-on-in-tech">To hear people in the industry talk</a>, most software companies don&#8217;t seem to care about making better<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> software, so where&#8217;s the market?&#8221; I&#8217;d convinced myself that if value didn&#8217;t flow through, end-to-end, from software buyers to sellers to programmers to me, there would be no audience for the book.</p><p>And the next thought after that was, &#8220;do I really want to help random software companies make <em>more</em> money, given how exploitative most of them are?&#8221; The answer is no, I don&#8217;t.</p><p>I recently realized that this framing is self-defeating and silly. Moreover, it just buys into the capitalist myth that says that money is the be-all and end-all of what we do and how we do it. That&#8217;s just not true.</p><p>The problem I&#8217;m trying to address is not that software companies aren&#8217;t making enough money. The problem is&#8230;</p><h2>Software development is stressful!</h2><p>It&#8217;s stressful for everyone involved, in all kinds of ways:</p><ul><li><p><strong>Managers:</strong> The project is late, and QA found 150 bugs in the last build!</p></li><li><p><strong>Programmers:</strong> The code makes no sense! Tests are failing and you can&#8217;t tell why. Your progress is delayed by handoffs, dependencies, clunky tools, and endless meetings.</p></li><li><p><strong>Users:</strong> The new UI is impossible to navigate! And it&#8217;s ugly!</p></li></ul><p>Stress comes from <strong>unpleasant surprises</strong>. These surprises happen whenever our mental models of the system don&#8217;t match reality. The problem is, when mental models and reality fight, <strong>reality always wins</strong>.</p><p>People often react to this by trying harder to control reality. This is the origin of Gantt charts, elaborate design documents, and fear-driven defensive programming. But these techniques just create frustration and busywork &#8212; and surprises often continue to happen anyway.</p><p>Fortunately, there is a better way. <strong>By building more accurate mental models, we can reduce the frequency of surprises</strong>, and equip ourselves to deal with the remaining ones in less-stressful ways.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> The techniques in <em>Process to Processes</em> focus on modeling systems and keeping them easy to model as they grow.</p><p>Maybe you&#8217;re not in the market for stress-reducing techniques. Maybe you&#8217;re one of the lucky few. &#8220;My job isn&#8217;t that bad. I clock in at 9 and clock out at 5. I get my work done. My boss likes me.&#8221; That&#8217;s great! But you might still be missing out, because&#8230;</p><h2>Software development can be joyful</h2><p>For me, joy in software development comes from a few different sources:</p><ul><li><p>seeing my own thoughts reflected back to me</p></li><li><p>being the conduit for a smooth flow of information, from intention to action to result to feedback</p></li><li><p>mastering a new skill</p></li><li><p>giving others the same opportunities for joy.</p></li></ul><p>I&#8217;ll never forget the thrill of writing my first program (well, &#8220;writing&#8221; &#8212;&nbsp;my dad helped a lot). All <a href="https://github.com/benchristel/BensVB/blob/main/BensVB/Ben1/ThePlane/ben1.frm#L79">the program</a> did was display a button that printed &#8220;It Worked&#8221; when you clicked it. But seeing that message appear caused me to <em>literally</em> jump for joy.</p><p>There is something incredibly reassuring about explaining your thoughts to a chunk of silicon, and having the chunk of silicon repeat them back to you as objects that change color and move and dance. It makes you feel smart, like you are not deluded, like your thoughts make sense. They <em>must</em> make sense, because you just taught <em>a system made out of math</em> how to think them.</p><p>Learning to program is hard. Learning to program professionally is even harder. But once you&#8217;ve practiced the relevant skills long enough, software development becomes a virtuosic improvisational performance. It&#8217;s <em>almost</em> effortless. Most of the time, you can just go with the flow, responding gracefully to whatever happens.</p><p>That sounds nice, but how can we get there? There&#8217;s no secret, really: just keep using the techniques for low-stress software development and gradually improving on them day by day. It&#8217;s like we&#8217;re climbing a mountain: there&#8217;s no magic trick that will get us to the top. You climb by taking a step, and then taking another step, over and over.</p><h2>It works better this way</h2><p>Some party poopers might find all this talk of joy a bit heretical. They don&#8217;t pay us to have fun! But joy in programming is not about goofing off, nor is it some extra, tacked-on activity that costs net time or money. <strong>Joyful programming is simply effective programming. </strong>We do it because it works. It is <em>more efficient</em> than the alternative.</p><blockquote><p>The kind of people who become programmers and developers have &#8216;fun&#8217; when the effort they have to put out to do a task challenges them, but is just within their capabilities. &#8216;Fun&#8217; is therefore a sign of peak efficiency. Painful development environments waste labor and creativity; they extract huge hidden costs in time, money, and opportunity.</p><p>If Unix were a failure in every other way, the Unix engineering culture would be worth studying for the ways it keeps the fun in development &#8212; because that fun is a sign that it makes developers efficient, effective, and productive.</p><p>&#8212;Eric S. Raymond, <em><a href="http://catb.org/esr/writings/taoup/html/ch01s05.html">The Art of Unix Programming</a></em></p></blockquote><p>That said, getting to a place where joyful efficiency can happen does take some upfront investment &#8212; e.g. giving teams space to learn and practice. I&#8217;ll talk about strategies for managing that in the book. For now, let me refer you to <a href="https://github.com/benchristel/benchristel.github.io/wiki/AlignmentTrap">my thoughts on getting out of the &#8220;alignment trap.&#8221;</a></p><p>In the next post, I&#8217;ll give a high-level overview of the qualities of code that enable low-stress, joyful software development. Stay tuned.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>I&#8217;ll talk about exactly how I define &#8220;better&#8221; in the next post.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>For example, although my code sometimes has bugs, it&#8217;s rare that someone finds one that I can&#8217;t diagnose on the spot. The fix usually follows shortly afterwards.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Stack, Queue, or Shrug?]]></title><description><![CDATA[How to navigate a mess]]></description><link>https://bensguide.substack.com/p/stack-queue-or-shrug</link><guid isPermaLink="false">https://bensguide.substack.com/p/stack-queue-or-shrug</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 16 Sep 2024 12:01:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was dealing with some legacy code today and wanted to jot down some thoughts about the process.</p><p>The process of modifying code usually goes like this:</p><ol><li><p>Try to make your intended change</p></li><li><p>Discover the structure of the existing code doesn&#8217;t accommodate your change very well.</p></li><li><p>&lt;do something about it&gt;</p></li><li><p>Finish making your change</p></li></ol><p>This post is about step 3. What do you do when faced with non-ideal structure?</p><p>You have basically three options:</p><ol><li><p><strong>Stack.</strong> You shelve your work in progress (e.g. using <code>git stash</code>) and focus on improving the structure, as a subgoal of your overall task. We might call this &#8220;prefactoring.&#8221; Once the structure is right, you return to your original goal.</p></li><li><p><strong>Queue.</strong> You add a note to your to-do list, so you&#8217;ll remember to come back to the structural problem later. Then you hack in your intended change any old way it will fit. When you&#8217;re done with that, you review your to-do list and work on each item in turn until it&#8217;s done.</p></li><li><p><strong>Shrug.</strong> You hack in your intended change and deploy to production.</p></li></ol><p>Each of these options has pros and cons.</p><h2>Stack</h2><p>The nice thing about stacking your work is that when you&#8217;re done, you&#8217;re <em>done</em>. Once your feature works, there are no loose ends to clean up; you&#8217;ve already taken care of that.</p><p>Stacking has many downsides, though.</p><ul><li><p>Deep stacks are scary. Halfway through refactoring, you may discover another subproblem you have to solve&#8230; and another, and another. Remembering where you are in the stack takes precious slots in your working memory (of which you have only 7, plus or minus 2). The more slots are taken up by navigation, the fewer you have available to think about the code.</p></li><li><p>Interruptions are costly. Once those 7 +/- 2 memory slots are swapped out, restoring them is a difficult, lossy process.</p></li><li><p>What&#8217;s more, you don&#8217;t know how deep the stack is going to grow, so you are constantly making strategic decisions based on incomplete information. At what point do you cut your losses, throw away your work in progress, and try one of the other strategies?</p></li><li><p>The tooling support for stacking isn&#8217;t great. <code>git stash</code> works about as well as it can, but you still get merge conflicts when you pop a stash, and that feels yucky. You thought you were about to <em>finish</em> a subtask, but now you have a new one: fixing the conflict.</p></li><li><p>Finally, there&#8217;s a risk that when you&#8217;re done prefactoring, the new structure won&#8217;t actually accommodate your feature. Your changes are based on some degree of speculation.</p></li></ul><p>For these reasons, I tend to prefer the next strategy, <strong>queueing</strong>.</p><h2>Queue</h2><p>There&#8217;s a lot to like about queueing:</p><ul><li><p>You&#8217;re usually taking small steps that are easy to reverse. The code is shippable after every step.</p></li><li><p>There&#8217;s less risk that you&#8217;ll make refactoring changes that aren&#8217;t helpful to your overall goal.</p></li><li><p>Interruptions aren&#8217;t as big of a deal; it&#8217;s easier to resume where you left off. </p></li><li><p>The tools are ubiquitous; everyone has a favorite to-do list technique.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p></li></ul><p>Downsides include:</p><ul><li><p>Sometimes you can&#8217;t figure out how to get even a hacky version of your feature working &#8212; which is the first step in a queueing approach. You <em>need</em> to improve the structure before you can move forward at all. In these cases, you have to start with a stack, and maybe switch to a queue later.</p></li><li><p>There&#8217;s a temptation to quit before finishing everything on your to-do list. Heck, there&#8217;s a temptation to quit before <em>starting anything</em> on your to-do list. There&#8217;s a voice in your head saying &#8220;Can you really justify spending time on refactoring? You were able to implement the feature, so the code must be fine! Just ship it!&#8221; Because of this, queueing can decay into the third strategy, <em>shrugging</em>. Queueing takes discipline.</p></li></ul><p>These two downsides are, interestingly, two sides of the same coin. Tempting as it is to leave our to-do list undone, that decision will come back to haunt us eventually. Someday, the hack-it-in approach will stop working, and then we&#8217;ll have to tackle a deep stack of fixes to make progress. <strong>When we have to stack, it&#8217;s because those who came before us didn&#8217;t queue.</strong></p><p>This brings us to the third approach, <em>shrugging</em>.</p><h2>Shrug</h2><p>Shrugging is my ad-hoc term for giving up on structure, and just throwing another stick on the dumpster fire of legacy code.</p><p>Shrugging often isn&#8217;t a deliberate decision. I tend to shrug when I mix the stack and queue approaches in an unstructured way, intending to come back and fix certain things but not writing them down or really committing to them. At some point, my code is working and I can&#8217;t remember everything I wanted to fix. Since the fixes don&#8217;t feel urgent or important anymore, the temptation to &#8220;just ship it&#8221; is strong.</p><p>Sometimes, though, shrugging <em>is</em> a deliberate decision:</p><ul><li><p>I don&#8217;t know how to improve the code, or don&#8217;t feel empowered to do it. Social pressure is a major factor here. I don&#8217;t want to mess with the structure of &#8220;someone else&#8217;s&#8221; code.</p></li><li><p>I am working within a pull-request workflow, and have to choose between:</p><ul><li><p>putting all my changes in one PR, which makes them harder to review and thus lengthens the delay before I can deploy them.</p></li><li><p>splitting my changes up into many refactoring PRs, which makes merging them all a pain. Also, people tend to review the newest PR first (because it&#8217;s the one they were last notified about) when I need to merge the <em>oldest </em>one first.</p></li></ul><p><br>In such cases I sometimes decide that refactoring isn&#8217;t worth the effort.<br><br>The mitigation for these problems is to pair on refactoring changes. Pair programming has many advantages, but one that I&#8217;ve come to appreciate recently is how it makes this dilemma go away. Pairing on a big refactoring expedition is much easier than reviewing the results of one after the fact. Plus, if you pair with the &#8220;owner&#8221; of the code you&#8217;re changing, the social aspects become easier to navigate too.</p><p></p><p>Of course, depending on your company&#8217;s culture, asking someone to pair can feel socially awkward too. I don&#8217;t have a solution for that yet.</p></li></ul><h2>Conclusions</h2><p>The following points are mostly meant as reminders to myself. Your mileage may vary.</p><ul><li><p>Separate refactoring (structure-only changes) from behavior changes. As in, put them in separate commits.</p></li><li><p>In general, prefer queueing to stacking when making a sequence of changes to legacy code. Keep the code shippable at all times.</p></li><li><p>Keep a to-do list. It can be very lightweight.  Bookmarking annoying code with a TODO comment only takes a second, and searching for those comments in your <code>git diff</code> is similarly easy.</p></li><li><p>Prefer pairing to async code review for refactorings.</p></li></ul><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>If you don&#8217;t have a favorite system yet, I recommend putting to-do comments in the code with a special tag like &#8220;FIXME,&#8221; and configuring your linter to fail if such comments are present. That way you&#8217;re sure to check everything off before shipping.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Code Quality: Hidden Costs, Unexpected Benefits]]></title><description><![CDATA[Cross-posted from benchristel.com.]]></description><link>https://bensguide.substack.com/p/code-quality-hidden-costs-unexpected</link><guid isPermaLink="false">https://bensguide.substack.com/p/code-quality-hidden-costs-unexpected</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 02 Sep 2024 12:02:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hCBl!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8967e7bd-33f4-4513-a9ac-79b9eba3b0f1_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Cross-posted from benchristel.com. Go read <a href="https://benchristel.com/posts/code-quality-costs-benefits/">the original article</a> if you want footnotes + the latest updates.</em></p><p>I spend a lot of time thinking about why there's so much bad code in the world, and what we can do about it. I hypothesize two reasons code quality tends to degrade:</p><ul><li><p>The costs of poor code quality are hard to see, because of how quality shapes decisions at the organizational level.</p></li><li><p>Executives and managers don't have the detailed knowledge they'd need to reap the benefits of good code. A strictly command-and-control organization can't take advantage of code quality; teams and individuals need local agency. Such an organization has little reason to prioritize code quality, and little hope of doing so effectively.</p></li></ul><p>These certainly aren't the only two causes of bad code, but they're the ones I'll focus on in this post.</p><h2>Hidden Costs</h2><p>Imagine you have two computers: an old clunker, and a new, top-of-the-line machine. The reason you keep the old computer around is that it runs business-critical software that would be too difficult to port to the new computer. Still, you try to touch it as little as possible, doing most of your work on the new computer.</p><p>The old computer is slow and annoying to use. When you're using it, it <em>feels</em> like it's costing you a lot of time. However, when you look objectively at how much time you're actually spending with it, you realize it's not actually <em>that</em> much time, in the scheme of things. You touch the old computer so infrequently that the inefficiencies don't add up to much.</p><p>Now, here's the thing: if the old computer were faster and easier to use, you'd use it more. You use it infrequently <em>because</em> it's inefficient. The behavior of the larger system (you plus the two computers) has adjusted to accommodate the relative efficiencies of each subsystem.</p><p>Software companies operate similarly. When a part of the codebase gains a reputation for being <a href="http://localhost:8080/terms/legacy-code.html">legacy code</a>, everyone tries to find ways to avoid touching it. Shims are inserted; substitutes are devised. Whenever possible, behavior is implemented somewhere else.</p><p>This leads to a vicious cycle: avoidance of legacy code leads to weird architectural decisions in other parts of the codebase, turning <em>them</em> into legacy code too. But the process is gradual. In the meantime, software development can continue, and the company can keep making money.</p><p>More perniciously, bad code directs attention and effort away from certain opportunities. If a feature or product doesn't "pencil out" &#8212; if the cost of implementing it would be impractically high, due to poor code quality &#8212; then it will never get worked on. In this situation, the cost of changing the bad code will never be paid, but the potential value of the feature won't be realized either.</p><p>So it's not that poor quality has no cost. The costs are simply invisible, because they show up as a <em>decrease in value</em> rather than an <em>increase in expenses</em>. How do you measure the cost of <em>not</em> working on code, of <em>not</em> building a feature, because it would be too expensive or risky to try? How do you even notice it? The problem is made worse because working in this kind of environment for a long time can cause a kind of <a href="https://en.wikipedia.org/wiki/Learned_helplessness">learned helplessness</a>, where you don't even see the opportunities that <em>are</em> available to you.</p><p>What's a responsible engineer to do in such a situation? Follow the Scouts' rule: no matter what code you're working on, <strong>leave it better than you found it.</strong> Or at least, don't make it worse. Although remediating the bad code <em>now</em> might not make economic sense, <em>preventing</em> it in the past would have (if only we had a time machine). We can't undo those past decisions, but we can start making better ones.</p><h2>Unexpected Benefits</h2><p>A few months ago, Kent Beck wrote an executive summary of his book <em>Tidy First</em>, titled <a href="https://tidyfirst.substack.com/p/the-surprise-factory">"The Surprise Factory."</a> The idea of the Surprise Factory is that tidy code produces delightful surprises, in the form of unexpected opportunities to add value to the company. (Kent later decided that "surprise" had too many negative connotations, so he changed the name to "The Gift Factory.")</p><p>Here's the problem that arises when this theory meets practice: although high code quality <em>does</em> produce opportunities to add value, executives can't see <em>which</em> specific opportunities are present. Code quality is not a scalar value; "good code" cannot make <em>every</em> imaginable change equally easy. Rather, we call code "good" when it can meet the <em>specific</em> demands placed on it. But executives aren't looking at the code, and can't see which specific demands it might be capable of meeting. The opportunities presented by the Surprise Factory are thus invisible to them.</p><p>Without a detailed understanding of the code, executives and managers only have a few crude knobs to turn: one knob might be labeled "more ambitious / less ambitious". Another knob might adjust which parts of the codebase or the product receive the most development effort.</p><p>The crudeness of these controls all but ensures that code quality will degrade over time. If executives respond to a high-code-quality, high-opportunity environment by simply turning up the "ambition" knob and directing effort toward the highest-quality parts of the codebase, the code will be ruined by developers trying to hit deadlines: it's unlikely that the opportunities coming out of the Surprise Factory will align perfectly with the the plans that trickle down from the executives' vision. But turning the knobs the other way would be folly: you'd burn out your programmers by making them work on the cruftiest code, and you'd have little to show for it in the end.</p><p>Again, what's a responsible engineer to do in this situation? I've come to the conclusion that it's <em>my</em> job to reveal value-adding opportunities to my employer, so they can take advantage of the surprise factory. I do this in a few ways:</p><ul><li><p>I suggest features that I think would be easy and valuable to add.</p></li><li><p>When my team is estimating stories, I point out cheaper alternatives to the proposed design.</p></li><li><p>I make delivery forecasts based on data, not vibes.</p></li><li><p>I cut out low-value work where possible.</p></li></ul><p>One caveat to this recommendation: in order to pull it off, you need design and product skills as well as engineering skills. Nothing's more annoying than a programmer who won't shut up about the weird feature or design idea that they want to shove into the product. However, it seems that the recent trend is for engineers to take on more design and PM responsibilities anyway &#8212; gone are the days of PMs writing Gherkin and designers insisting on pixel-perfection. For better or for worse, we programmers have the power now. We should learn to wield it.</p>]]></content:encoded></item><item><title><![CDATA[Website!]]></title><description><![CDATA[a hat rack for my internet hats]]></description><link>https://bensguide.substack.com/p/website</link><guid isPermaLink="false">https://bensguide.substack.com/p/website</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Wed, 07 Aug 2024 12:01:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ia6h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After two and a half decades online, I finally have my own website: <a href="https://benchristel.com">benchristel.com</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ia6h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ia6h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 424w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 848w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 1272w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ia6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png" width="1167" height="555" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:555,&quot;width&quot;:1167,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:62148,&quot;alt&quot;:&quot;A screenshot of benchristel.com, showing the tree graphic and list of links on the homepage&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A screenshot of benchristel.com, showing the tree graphic and list of links on the homepage" title="A screenshot of benchristel.com, showing the tree graphic and list of links on the homepage" srcset="https://substackcdn.com/image/fetch/$s_!Ia6h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 424w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 848w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 1272w, https://substackcdn.com/image/fetch/$s_!Ia6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d68da90-5a4e-4a39-8b72-3efb5ef6cdcd_1167x555.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Heavily inspired by Richard P. Gabriel&#8217;s <a href="https://dreamsongs.com/">Dreamsongs.com</a>, which you should definitely check out.</figcaption></figure></div><p>I wanted to have one hat rack where I could hang all my internet hats. benchristel.com is the hat rack.</p><p>(This idea is analogous to the <strong>entrypoint document</strong> pattern for software projects, which I need to write a <a href="https://benchristel.github.io/process-to-processes/">book</a> chapter about someday soon.)</p><p>You won&#8217;t find much on the site itself, yet, except <a href="https://benchristel.com/portal.html">a big pile of links</a> I&#8217;ve bookmarked. But I wanted to let you know about it because it relates to my near-term plans for this newsletter.</p><p>In short: I want to be less dependent on Substack. I like publishing on Substack, and I have no plans to stop doing so, but I don&#8217;t want it to be the <em>only</em> place where people can find my writing. I&#8217;d like to see what I can do with <a href="https://indieweb.org/">indieweb</a> technologies, and generally get involved with the indieweb scene a bit more, because I think it&#8217;s important for information-sharing to be decoupled from surveillance capitalism. So I want to shift my mindset towards treating my book and website as primary, and this newsletter as secondary.</p><p>What that means, concretely, is:</p><ul><li><p>I&#8217;ll continue to write chapters of <a href="https://benchristel.github.io/process-to-processes/">my book, </a><em><a href="https://benchristel.github.io/process-to-processes/">Process to Processes</a></em>, and cross-post them here.</p></li><li><p>I&#8217;ll also write standalone essays, which I&#8217;ll post on <a href="https://benchristel.com/">benchristel.com</a> and maybe also cross-post here if they seem germane.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p></li><li><p>Treating benchristel.com as the primary repository of my work will make it easier for me to organize my writing, keep it up to date, and format it exactly how I want (I&#8217;m still working on this last part).</p></li></ul><p>Long-time subscribers have seen how good I am at following a plan (i.e. not good). With that in mind&#8230; that&#8217;s my plan.</p><p>If you like the current format of this newsletter, don&#8217;t worry. From your perspective, nothing will change.</p><p>However, if you&#8217;d like to subscribe directly to benchristel.com, in order to get <em>up-to-the-megasecond news </em>about what I&#8217;m working on, <strong>you can</strong>! Just drop the URL, <code>https://benchristel.com</code>, into your favorite RSS reader. And if you have no idea what I&#8217;m talking about, you can also <a href="https://benchristel.com/subscribe.html">subscribe with your email, here</a>.</p><p>(If you <em>do</em> happen to have a favorite RSS reader, I&#8217;d love to know what it is. Let me know in the comments.)</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>This is the &#8220;publish on your own site, syndicate elsewhere&#8221; (<a href="https://indieweb.org/POSSE">POSSE</a>) pattern.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Alexander's Surprising Solution]]></title><description><![CDATA[Uniting aesthetics and function]]></description><link>https://bensguide.substack.com/p/the-alexandrian-solution</link><guid isPermaLink="false">https://bensguide.substack.com/p/the-alexandrian-solution</guid><dc:creator><![CDATA[Ben Christel]]></dc:creator><pubDate>Mon, 29 Jul 2024 12:01:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!euOn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p> Hello! I&#8217;m Ben Christel, and you&#8217;re reading Ben&#8217;s Guide to Software Development. Today&#8217;s post is the eighth and final episode of a series on the architect-builder Christopher Alexander, and how his philosophy applies to software development. Here are the first seven posts:</p><ul><li><p><a href="https://bensguide.substack.com/p/i-am-confused">I am confused</a></p></li><li><p><a href="https://bensguide.substack.com/p/the-christopher-alexander-post">The Christopher Alexander Post</a></p></li><li><p><a href="https://bensguide.substack.com/p/the-15-properties-in-software-part">The 15 Properties in Software, Part 1</a></p></li><li><p><a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-d8e">The 15 Properties in Software, Part 2</a></p></li><li><p><a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-1da">The 15 Properties in Software, Part 3</a></p></li><li><p><a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-859">The 15 Properties in Software, Part 4</a></p></li><li><p><a href="https://bensguide.substack.com/p/adaptation">Adaptation</a></p></li></ul><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!euOn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!euOn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!euOn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!euOn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!euOn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!euOn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg" width="684" height="524.1379310344828" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:522,&quot;resizeWidth&quot;:684,&quot;bytes&quot;:96863,&quot;alt&quot;:&quot;A group of people sits around a table set with teacups and saucers. Christopher Alexander sits on the right, clapping his hands and laughing as if at a hilarious joke.&quot;,&quot;title&quot;:&quot;A group of people sits around a table set with teacups and saucers. Christopher Alexander sits on the right, clapping his hands and laughing as if at a hilarious joke.&quot;,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A group of people sits around a table set with teacups and saucers. Christopher Alexander sits on the right, clapping his hands and laughing as if at a hilarious joke." title="A group of people sits around a table set with teacups and saucers. Christopher Alexander sits on the right, clapping his hands and laughing as if at a hilarious joke." srcset="https://substackcdn.com/image/fetch/$s_!euOn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 424w, https://substackcdn.com/image/fetch/$s_!euOn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 848w, https://substackcdn.com/image/fetch/$s_!euOn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!euOn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe265e97a-b5bc-4698-b707-547e9b6ae392_522x400.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Christopher Alexander, the life of the party</figcaption></figure></div><p>I feel pretty silly for not re-reading what Christopher Alexander had to say on the matter, back when I published the question that started this whole series. The question, as I then stated it, was, <strong>&#8220;why do the same 15 properties of structure that characterize beautiful buildings also appear in healthy software systems?&#8221;</strong> In this final chapter of my series on Christopher Alexander, I want to analyze his solution to this puzzle.</p><p>The question I posed is a special case of a more general one: &#8220;what is the relationship between fact and value?&#8221; Or in other words, &#8220;what is the relationship between what works and what&#8217;s good?&#8221; This question is an ancient one, but it has only recently become urgent. The modern era has landed us neck-deep in planet-destroying utilitarianism and impractical, cloying style, and we need to find a way out before we alienate ourselves into oblivion.</p><p>As I dug into the chapter of <em>The Nature of Order</em> where Alexander presents his solution to this problem, I remembered why it didn&#8217;t click for me five years ago, when I first read it. It struck me at first as too vague, too woolly and &#8220;spiritual&#8221; to be plausible or falsifiable, so I dismissed it and forgot about it. But now, after <a href="https://bensguide.substack.com/p/the-15-properties-in-software-part">thinking</a> <a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-d8e">through</a> <a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-1da">many</a> <a href="https://bensguide.substack.com/p/the-15-properties-in-software-part-859">examples</a> in several different domains, Alexander&#8217;s solution seems far less mysterious to me, and I find myself agreeing with him, pretty much the whole way through.</p><p>I&#8217;m mostly going to just roll the tape and let him talk, though I will occasionally pause to interject my own commentary. Here is how he introduces the topic, on page 404 of <em>The Phenomenon of Life</em>.</p><blockquote><p>No building (and no part of any building) has real life unless it is deeply and robustly functional. What I mean by this, is that the beauty and force of any building arises always, <em>and in its entirety,</em> from the deep functional nature of the <a href="https://bensguide.substack.com/i/143763936/centers">centers</a> that have been created.</p><p>In nature there is essentially nothing that can be identified as a pure ornament without function. Conversely, in nature there is essentially no system that can be identified as functional which is not also beautiful in an ornamental sense. In nature there simply is no division between ornament and function. [. . .]</p><p>I shall try to show that the functional behavior of buildings, the human life present in them, like its geometry, can <em>all</em> also be understood in terms of wholeness. That means that emotion, movement, light, comfort, climate, balance of functions, the ability of a room to accommodate the behavior in the room, the engineering structure, the manufacturing &#8212; <em>all these practical matters can be understood in terms of <a href="https://bensguide.substack.com/i/143763936/centers">centers</a></em>. [. . .] All of it can and must be understood as something geometric happening in space.</p><p>During the early and middle 20th century, the idea of function was for the most part understood in a mechanistic spirit. In trying to work out what a building ought to do, how to analyze its way of working, one had the approach that the building&#8217;s functions were to be described by a kind of shopping list of &#8220;goals.&#8221; These goals were defined by the architect or engineer, then achieved.</p></blockquote><p>There&#8217;s a clear parallel to agile software development here: Alexander&#8217;s &#8220;goals&#8221; correspond to our features, user stories, epics, and perhaps KPIs (key performance indicators).</p><blockquote><p>However, there were unsolved puzzles inherent in this idea of needs or goals. Those of us who made lists of functions were aware that these lists were inherently arbitrary (dependent on the architect or client who made them, their forgetfulness, lack of insight, etc.) Where was the <em>real</em> list of needs?</p></blockquote><p>User stories are, of course, only a guess &#8212;&nbsp;an approximation of what the user <em>might</em> need, <em>might</em> do, <em>might</em> experience. A software project can deliver everything that was planned, on time and under budget, and still fail because the user stories and UI designs didn&#8217;t reflect the real set of needs. There seems to be no way around this. If you try to, say, analyze the requirements more deeply before writing user stories, you get into an infinite regress. How do you know the requirements document is correct?</p><blockquote><p>I remember making a long list of some 390 requirements which were to describe the ways in which [Bay Area Rapid Transit] stations could malfunction. But still, there was an intuitive sense that such a list might be wrong, might be missing items, might be profound or shallow. [. . .] <strong>Goals were always arbitrary in some essential way, which could not be mended.</strong></p><p>[emphasis added]</p></blockquote><p>Alexander singles out this focus on goals as a cause of the schism between beauty and function in architecture:</p><blockquote><p>There were further difficulties. The list of needs or goals, no matter how carefully stated, could only with difficulty be connected to the physical form of a building. [. . .] When it came to the physical beauty, ornament, gracefulness of appearance, these matters, obviously important, were in a different category.</p></blockquote><p>To continue the software analogy: a list of user needs, on its own, doesn&#8217;t tell you how to design your software internally, and it doesn&#8217;t even tell you what the UI should look like. Under this paradigm, the form of the thing (both internal and external), and therefore any aesthetic or emotional appeal that it has, has only a tenuous relationship to its function. If form is considered at all, it must be <em>after</em> the functional requirements of the software have already been determined. The form may then succeed or fail on its own merits, but that has nothing, supposedly, to do with its function. In this view, form for form&#8217;s sake is mere ornament &#8212; something you tack on after the fact, to &#8220;make it pretty.&#8221;</p><p>Yet this is unsatisfying, and in fact unworkable, because <em>the form creates the function of the program</em>. The program cannot function, and in fact cannot exist at all, until there is form. So if we try to separate function and form into distinct &#8220;phases&#8221; of design, we end up slapping together haphazard forms in the &#8220;function&#8221; phase, and then trying to patch them up in the &#8220;form&#8221; phase. The result is a system that neither works well nor feels good.</p><p>And we <em>still</em> have the problem that the goals we set upfront weren&#8217;t complete or correct to begin with. Sigh.</p><p>As unsatisfactory as this is, it&#8217;s hard to see how we could do better. Under the current paradigm, it seems impossible to create both beauty <em>and</em> function at a single stroke. I say <em>seems</em> because the problem lies not in our abilities, but in our <strong><a href="https://bensguide.substack.com/i/143550464/views">views</a></strong>: our views about the nature of reality, about what is subjective or objective, about the creative process, and about what it means for a building or software system to &#8220;work.&#8221;</p><blockquote><p>During the 20th century, the possibility of finding ways of designing or thinking about beauty and function in one breath seemed remote and unattainable. It was not possible, intellectually, because we did not have the right intellectual tools. It was not possible, artistically, because we could not think our way into a unitary frame of mind where the two could be fused, unified, in works of beauty which worked profoundly well. That was the state of architecture, almost without change, throughout the 20th century.</p></blockquote><p>By now you can probably see what Alexander is about to do: he wants to unify beauty and function by finding a way to get rid of the list of &#8220;goals,&#8221; or at least to treat it with a healthy irreverence. Now let&#8217;s see how he does it:</p><blockquote><p>[W]ithin the view of order which I have put forward in this book it is possible, in principle, to unify these two broken halves. It is possible to think of architecture in  a single way where beauty and function &#8212; both contributing to life &#8212; can be understood as a single, unbroken whole.</p><p>Function, like wholeness itself, is all based on centers. [. . .] As something lives, acts in the world, interacts with the world, different centers appear and disappear. Some are moving, some are temporary. The flux of these moving, transitory centers, and their appearing and disappearing, is the process we call life.</p><p>The process we call &#8220;function&#8221; is the process by which the static system is &#8212; or is not &#8212; in harmony with this moving system of centers that we call life. [. . .] As cars cross a bridge, they form centers. Each car in itself is a center; the stream of cars forms centers; a traffic blockage is a center. The road system, which has its own geometric centers, is either harmonious, or not harmonious, with the system of cars that are parked, moving, standing, and so on.</p><p>When they are harmonious and co-adapted, we call the system functional.</p></blockquote><p>In other words, there is only one kind of harmonious order, which is characterized by the fifteen properties. When we look at this kind of order &#8220;from the outside,&#8221; as an observer, we see wholeness, beauty, and good function. When we experience the same kind of order &#8220;from the inside,&#8221; by actually <em>living in</em> a system with the 15 properties &#8212; by actually <em>being</em> one of the dynamic centers that creates the life of the system &#8212; then we <em>feel</em> whole, at peace with ourselves, free, alive.</p><p>The highest purpose of a building, or a software program, is to strengthen this feeling of life in the people around it. And the way that the building or program creates life is very simple: it does it just though its form, through its geometry. Only its geometry, and nothing else.</p><p>This is perhaps easiest to see in software systems. The code of a running software application can be viewed as consisting of nothing but pure, abstract structure, like virtual <a href="https://en.wikipedia.org/wiki/Tinkertoy">Tinkertoys</a>. The <a href="https://en.wikipedia.org/wiki/Church%E2%80%93Turing_thesis">Church-Turing thesis</a> proves the validity of this view: you can compute anything that can be computed with nothing but an elaborate tree of <a href="https://en.wikipedia.org/wiki/Lambda_calculus">lambdas</a>. So it is <em>only</em> structure, and the order of the structure &#8212; the way it is put together, the way the different parts relate &#8212; that determines the <a href="https://bensguide.substack.com/p/behavior">behavior</a>, and thus the life, of the whole system.</p><p>In the same way, the life of a building comes just from its structure. The placement of the building on the site, its relationship to its neighbors, the proportions of the rooms, their arrangement, the placement of the windows and doors and pathways, the way sunlight falls in the rooms, the furniture, the trim, the way the light interacts with materials and human eyes &#8212;&nbsp;it is all structure. As we interact with this structure, we label some of our experiences &#8220;functional&#8221; and others &#8220;aesthetic.&#8221; But those are just labels. The fundamental thing is the geometry of the structure, and the interactions it creates. In other words, the fundamental thing is the life of the building.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Qhx5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Qhx5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Qhx5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg" width="500" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:355,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;https://i.pinimg.com/originals/dd/7c/49/dd7c49192915f9961805277721c9af25.jpg&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="https://i.pinimg.com/originals/dd/7c/49/dd7c49192915f9961805277721c9af25.jpg" title="https://i.pinimg.com/originals/dd/7c/49/dd7c49192915f9961805277721c9af25.jpg" srcset="https://substackcdn.com/image/fetch/$s_!Qhx5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Qhx5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4cdee847-ffe3-48d3-ac67-7512769eee66_500x355.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A &#8220;farmhouse kitchen&#8221; designed by Christopher Alexander</figcaption></figure></div><p>To see the inadequacy of the &#8220;functional&#8221; and &#8220;aesthetic&#8221; labels, consider the life of a living room and the furniture and people in it:</p><ul><li><p>A living room chair may be comfortable, with cushions and upholstery that are pleasant to sit on. Is this function or aesthetics?</p></li><li><p>The seating in a living room might be arranged around a hearth &#8212; a <strong>strong center</strong> &#8212; and might form a protective <strong>boundary</strong> that separates the seating area from the circulation paths at the edge the room. This arrangement is functional (conducive to conversation) but it is also beautiful <em>as pure abstract structure</em>: it makes the room feel cohesive, balanced, and harmonious. So is it functional, or aesthetic?</p></li><li><p>The hearth itself can be a beautiful center, but it is also practical: in cold climates it provides needed heat to the house. It also provides a focal point for the room: something for people to gather around, and to look at as they talk. But does that satisfy a functional need? Or an aesthetic one?</p></li><li><p>All of these properties of the room encourage people to pause there, to sit down, to relax and talk and generally be convivial. Is this function or aesthetics?</p></li></ul><p>These questions don&#8217;t have clear answers, but fortunately, under Alexander&#8217;s paradigm, they don&#8217;t need to be asked. &#8220;Function&#8221; and &#8220;aesthetics&#8221; are concepts we&#8217;ve imposed on a universe that simply doesn&#8217;t care to distinguish between them. Since we made them up, we are free to get rid of them.</p><p>To be sure, there are some aspects of life that seem almost purely aesthetic, or almost purely functional. The distinction exists in our modern culture for a reason. But in a paradigm that does away with the distinction, we can still recognize these different aspects of life while also seeing that they are <em>both</em> life, and that they both arise from the same structural properties of centers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7XFp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7XFp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7XFp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg" width="722" height="480.4581818181818" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:550,&quot;resizeWidth&quot;:722,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;EISHIN CAMPUS&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="EISHIN CAMPUS" title="EISHIN CAMPUS" srcset="https://substackcdn.com/image/fetch/$s_!7XFp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7XFp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3af26138-4adf-4f11-bd7e-54c3333fee3b_550x366.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Unity of beauty and function: cherry trees blooming on the Eishin high school campus, designed and built by Christopher Alexander.</figcaption></figure></div><p>When you view the world from this perspective, everything snaps into focus. Beauty and function are not separate. They are simply two ways of describing a spectrum of experience. That experience is determined by the structure of the system, because <em>everything</em> is determined by the structure of the system.</p><blockquote><p>The functional behavior of each living room is almost geometrical in nature. What makes the room work is the geometrical intensity, vibrancy, of its living centers, their degree of life. The fifteen properties, the field of centers, and the wholeness not only control the way that beautiful buildings look. <em>They thoroughly and completely determine the way that buildings work</em>.</p><p>Altogether I believe the functional life of buildings is created by the same field effect among centers which creates the field of centers in an ornament. Each functional &#8220;problem&#8221; is solved by the cooperation or integration of centers which arise within the building dynamically, while it is working. The field of centers supports not only everything we commonly call ornament in a building but also everything we commonly call function. [. . .] What we call ornament and what we call function are simply two versions of one more general phenomenon.</p><p>&#8212;<em>The Phenomenon of Life</em>, p. 415</p></blockquote><p>Equipped with this view, the designer, instead of having to grapple with two questions &#8212; &#8220;how do we make it work&#8221; and &#8220;how do we make it beautiful&#8221; &#8212; only needs to consider one: <strong>&#8220;what should my next change to the form be, to create the greatest feeling of life in the whole?&#8221;</strong> This is a difficult question to answer, but, as I will attempt to show in my <a href="https://benchristel.github.io/process-to-processes/">book</a>, not as difficult as it seems. And it is <em>far</em> more workable, practically, than the alternative under which we are currently suffering.</p><p>So, to answer my original question:</p><blockquote><p>Why do the same 15 properties of structure that characterize beautiful buildings also appear in healthy software systems?</p></blockquote><p>I think Alexander would say that the 15 properties describe a certain kind of structure: orderly, harmonious, whole &#8212; whatever you want to call it. A kind of structure that <em>makes sense</em>. Sometimes we call this sense-making &#8220;beauty&#8221; and sometime we call it &#8220;function&#8221; and sometimes we call it &#8220;health&#8221; or &#8220;wholeness&#8221; or &#8220;life.&#8221; It is all the same thing, the same kind of order.</p><p>And to answer Alexander&#8217;s rhetorical question,</p><blockquote><p>Where was the <em>real</em> list of needs?</p></blockquote><p>I would say that there is only one real need, from which all the other specific needs derive: the life of the whole.</p><p><em>This</em> is the need that I constantly return to when creating software. Whenever I am thinking about what to do and how to do it, I always evaluate the result (or my guess at what the result will be) by the life that it creates. By which I mean: is the resulting interaction more pleasant than the status quo? Or, more profoundly: does it make me feel more alive, more of a person, more connected to the work, more truly at ease being myself?</p><blockquote><p>What is involved in this approach is that we pay attention not only to the functions themselves, but also (and rather) to the overall life of the system as a whole. The approach treats the space as a whole, and tries to make it more harmonious, more alive, more unified <em>as a</em> <em>whole</em>.</p><p>&#8212;<em>The Phenomenon of Life</em>, p. 419</p></blockquote><p>As strange as it sounds, designing for life works. Here is Alexander&#8217;s account of how he designed housing in Peru in 1969, beginning with a home-stay with a Peruvian family:</p><blockquote><p>Before going to Lima, we read various anthropologists&#8217; reports about Peruvian society. It all seemed very exotic, but none of it seemed even remotely useful when we were living in our Peruvian families. For instance, ethnographic reports claimed that Peruvians kept the windows closed at night, even in hot weather, because they didn&#8217;t want ghosts to come through the windows with the night air. I never heard anyone say anything like this while I was living in the district of Victoria in Lima.</p><p>I found that I could imagine the Peruvians&#8217; feelings best just by being one of them. For instance it was a dangerous place, and it wasn&#8217;t safe to leave windows open at night. That was really all the explanation one needed for why the windows had to be closed, even when it was sweltering hot. I found that if I looked at life from the point of view of being one of them, my own feelings, and my own knowledge of what had to be, was more reliable than anything else as an indicator of what was needed for a Peruvian family.</p><p>The <em>comedor</em> (dining room) in the middle of the house, where everyone came by, watched TV, or sat and talked on the way in or out, was a wonderful place. Being a member of that family, I knew that I needed and wanted such a room &#8212; and I could feel exactly where it needed to be in the house (in the middle of everything). [. . .] I, myself (as Chris Alexander) didn&#8217;t have a house like that, and I don&#8217;t want a house like that &#8212; because for me, in Berkeley, with my family, it would not have made sense &#8212; it would not have been part of things, or part of the way my life works. But as a member of that Peruvian family, in the Peruvian culture, in the context of that family which I was a part of, it <em>did</em> make sense. It was natural, necessary, and I could feel its necessity, as part of <em>me</em>.</p><p>&#8212;<em>The Process of Creating Life</em>, pp. 350&#8211;51</p></blockquote><p>By Alexander&#8217;s account, the process succeeded:</p><blockquote><p>[P]eople in Peru said that our pattern language and our houses we designed from the pattern language were a more accurate reflection of Peruvian reality than even the Peruvian architects had managed.</p><p>People wondered how we did it. But it was really very simple [. . . .] We identified the centers by getting so deeply into the situation that we could feel, in our own bodies, just which ones needed to be there.</p><p>The essential technique in the observation of centers, in any social situation, and in any culture, is to allow the feelings to generate themselves, inside <em>you</em>. You have to say, &#8220;What would I do if I were one of the people living here, what would it be like for me?&#8221; thus inserting yourself into the situation, and then using your own common sense and feelings as a measuring instrument.</p><p>Of course you must always check with people, explicitly. You cannot assume you are right. You have to check. On the other hand, checking doesn&#8217;t mean just do what people say; their own sense of what is involved can also be in error. One must always go to the root, asking what is likely to create the most life, and maintaining a cautious skepticism, even while pursuing these difficult and soul-searching questions.</p><p>&#8212;<em>The Process of Creating Life</em>, p. 352</p></blockquote><p>The process Alexander is describing here &#8212; and this is in <em>1969</em>, remember &#8212; foreshadows Extreme Programming, which recommends having an <strong>onsite customer </strong>for the software you&#8217;re building. The idea behind the onsite customer is that if programmers live side-by-side with one of their users, day in and day out, seeing how they work, and experiencing their joys and frustrations, they will develop empathy for those users. Empathy makes it much more likely that the team will build a great product. This technique of &#8220;feeling into&#8221; another person&#8217;s experience, by almost literally standing in their shoes, is exactly the same as what Alexander did in Lima.</p><p>Christopher Alexander&#8217;s philosophy may seem pretty &#8220;out there&#8221; at first glance, but putting it into practice feels rather ordinary and commonsense. In a way, what I&#8217;ve written above isn&#8217;t all that far-fetched, compared to the way we routinely do things in software development today. Ideas like service design, incremental development, refactoring, user interviews, recognition of the <a href="https://lawsofux.com/aesthetic-usability-effect/">aesthetic-usability effect</a> &#8212; these <em>are</em> the state of the art in at least some software companies. I sometimes feel that we are close to achieving Alexander&#8217;s vision, and that the philosophy I&#8217;m describing is a short step away from how (good) modern software development currently works.</p><p>But we are <em>not</em> close. Though some of us may be heading in the right direction, there is a deep chasm between how we <em>feel</em> about our work today, and how we need to feel, in order to generate the kind of world that Alexander envisioned. I said we are one step away, but in reality that step would be a leap: a quantum leap, into a new paradigm, and an entirely new way of viewing our relationship with the world. We aren&#8217;t prepared to make that leap yet.</p><p>I remain hopeful, though, that someday soon, we will be.</p><p>.</p><p>.</p><p>.</p><p>THE END of the Christopher Alexander series.</p><div><hr></div><p><em>What would be required for us to cross the chasm into the Alexandrian paradigm, in which fact and value are unified? I hope to explore that topic in a future post. For now, here is a sneak peek:</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yRsP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yRsP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 424w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 848w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 1272w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yRsP!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png" width="1200" height="381.5934065934066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:463,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:241183,&quot;alt&quot;:&quot;Two diagrams showing reinforcing feedback loops. On the left, \&quot;The Vicious Cycle of Alienation\&quot;: Alienation, Aversion, Callousness, Lack of care, mess and broken windows. On the right, \&quot;The Virtuous Cycle of Oneness\&quot;: Oneness, gusto, kindness, attentive care, living structure&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-large" alt="Two diagrams showing reinforcing feedback loops. On the left, &quot;The Vicious Cycle of Alienation&quot;: Alienation, Aversion, Callousness, Lack of care, mess and broken windows. On the right, &quot;The Virtuous Cycle of Oneness&quot;: Oneness, gusto, kindness, attentive care, living structure" title="Two diagrams showing reinforcing feedback loops. On the left, &quot;The Vicious Cycle of Alienation&quot;: Alienation, Aversion, Callousness, Lack of care, mess and broken windows. On the right, &quot;The Virtuous Cycle of Oneness&quot;: Oneness, gusto, kindness, attentive care, living structure" srcset="https://substackcdn.com/image/fetch/$s_!yRsP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 424w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 848w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 1272w, https://substackcdn.com/image/fetch/$s_!yRsP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda924e97-2dd4-4e5f-b3f9-a1a89e283066_2757x876.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"></figcaption></figure></div><p></p><div><hr></div><p><em>Okay, that&#8217;s all for now, folks! Back to book-writing for me. And, uh, all my other projects. I have a lot of irons in the fire right now. I hope to have things in a shape where I can share some of what I&#8217;m doing with you next week.</em></p><p><em>Thanks for being here.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://bensguide.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://bensguide.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item></channel></rss>