<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Matthew Hayes</title>
    <link>https://whypeople.xyz/</link>
    <description>Writings about software, systems, and mediums by Matthew Hayes.</description>
    <language>en-us</language>
    <atom:link href="https://whypeople.xyz/rss.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Sun, 26 Jul 2026 00:00:00 GMT</lastBuildDate>

    <item>
      <title>Voice vs Typing — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#voice-vs-typing</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#voice-vs-typing</guid>
      <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’ve seen propaganda around voice dictation being faster than typing,
          which is why one should adopt it as their primary input mechanism. A
          few months ago I was like this, but ultimately reverted back to just
          typing everything. There’s 2 reasons for this:
        </p><ul>
          <li>
            Talking constantly feels more physically tiring on the body than
            typing.
            <ul>
              <li>
                See me after meetings in which I talk a lot. It’s usually harder
                to context switch back to deep work due to the accumulated
                exhaustion.
              </li>
            </ul>
          </li>
          <li>
            I am biased (by evidence of this blog) towards writing, and I’ve
            always been better at communicating complex ideas through writing as
            a result.
          </li>
        </ul><p>
          Personally, I find it’s often hard to convey many of my more
          contrarian ideas in an oral medium, unless I’m well acquainted with
          the person or group I’m speaking to. Perhaps this is because an oral
          medium tends to give its way more to “System 1” (visceral) kinds of
          thinking than writing which lends its way to more “System 2” (deep)
          thinking. Often, contrarian ideas are not easily accessible in
          visceral mode, because it takes active thought to go against things.
        </p><p>
          I think another aspect is that writing is also visible. For example, I
          could re-read everything I just wrote before writing this sentence,
          and a pure reader can re-read things as many times as they want.
          Perhaps this feedback is what encourages more “System 2” thinking.
        </p><p>
          The downside of course is that in theory productivity slows, but I’m
          not so sure by how much in practice when we consider overall
          productivity across a work session. Many of the tasks I give GPT 5.6
          Sol run for at least half an hour, and stem from only a single or
          handful of messages with occassional steering during the task.
          Therefore, I think optimizing my WPM would be less impactful than
          optimizing the model’s throughput (TPS)/token efficiency when we
          consider the overall amount of work being done.
        </p><p>
          Of course, this is not a piece on whether or not voice interfaces are
          useless, because that is false. A great example of these are "hands
          free" situations, devices which aren't designed to have a keyboard and
          are mostly interacted with ephemerally, or collaborative interfaces.
          My take is mostly based on personal preference towards how I
          communicate with other humans/agents digitally.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Code is a Representation — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#code-is-a-representation</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#code-is-a-representation</guid>
      <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Since the monthly “reading code” debate seems to be going around
          again, I thought I would add absolutely zero value to it by writing
          this note. As I’ve mentioned before, code, your editor, etc. is just a
          visual representation of a system. It is literally a UI in the most
          literal sense possible for your to be able to edit and understand what
          is going on.
        </p><p>
          The interesting thing is that representations are interchangeable, and
          the right one very much depends on the system’s domain (I really like
          <a href="https://vimeo.com/64895205">Stop Drawing Dead Fish</a>). So
          while handwriting code in general purpose programming remains fun as
          an art form, it hasn’t really ever been the best representation for
          understanding and editing. Just the most unquestioned one. For the
          record, my statements in this paragraph obviously don’t support a
          general purpose successor to today’s code either because of domain
          specificity (eg. Visual things need visual and not textual
          representations).
        </p><p>
          A lot of talk has gone around about “writing slop to validate code”,
          which I interpret more as “writing formal verification tools that
          would’ve been too time consuming before”. I generally agree with this
          sentiment, and I often do these things myself. For instance, I find
          myself mocking interfaces a lot less because it’s simply easier to
          write test infrastructure using live implementations. One of my most
          recent internal projects even had an entire dedicated library just for
          testing IPC, Websocket, and HTTP interactions because they were so
          common place. One of my upcoming open source projects has an entire
          Python CLI alongside the main Swift package that I wouldn’t have had
          time to write by hand just to be able to create an export of a model
          for various inference engines (including Apple’s new CoreAI
          framework). These are quality improvements that are only worth it from
          a time standpoint because of agents. (Furthermore, it’s worth
          mentioning that as humans we are quite bad at making decisions due to
          a lot of psychological/biological factors. This is where having tools
          that don’t succumb to those same flaws can help. See the
          <a href="https://en.wikipedia.org/wiki/Hungry_judge_effect">hungry judge effect</a>
          for an example of such a biological factor.)
        </p><p>
          How one interacts (ie. reading or writing) with code today largely
          depends on what is being built seems to be my line of thinking. For
          one, if the end product is an SDK of some kind, then you’ll be forced
          to at least read the code for the public API since code literally is
          the product. For complex and safety critical domains (ie. hard to get
          write/dangerous to get wrong), it obviously still makes sense to read
          and verify everything alongside having formal verification tools to
          make that process more robust. However, for apps where the code-level
          implementation consists of mostly “gluing libraries/frameworks
          together”, it’s probably becoming less important since the difficult
          part for those was never the code in the first place, but rather
          architecture and domain complexity. (In fact, the intensive focus on
          code may have taken away necessary focus on architecture! This is
          something I’ve certainly been guilty of in the past.)
        </p><p>
          I still like handwriting some amount of code whenever I have spare
          time to, mostly for learning purposes and because it’s still fun. (eg.
          When learning MLX Swift recently, I opted to write a model by hand
          before I allowed myself to use agents for the same thing, this gave me
          a better understanding of the framework’s mechanics.)
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>A Mobile OS-Level Model Registry — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#a-mobile-os-level-model-registry</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#a-mobile-os-level-model-registry</guid>
      <pubDate>Wed, 22 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If more mobile apps keep downloading the same models that are
          considered large assets for mobile standards (1-4 GB), it would be
          wise to find some way to share them at an OS level unless we want to
          suffer a tradegy of the commons when it comes to app storage sizes.
          Furthermore, it would be a better user experience if a new app can
          simply detect if a model has already been downloaded, and avoid doing
          so itself as a result. Both Apple and Google already do this for their
          on-device models, so it’s clearly possible at some scale. The question
          is how practical can it be at larger scales?
        </p><p>
          One primary issue is that the “same model” does not imply “same
          weights”, so it’s possible to have multiple copies a single model with
          different quantizations, fine-tunes, etc. This is not even considering
          the proprietary file formats from some engines, which would cause
          further fragmentation.
        </p><p>
          It wouldn’t be practical to run a single OS-level inference server on
          mobile either due to the disparity in the amount of engines that
          exist. Any server would have to have a set of “known engines”, or an
          ability to link new engines dynamically somehow (perhaps in a similar
          manner to dynamic SQLite extensions). The latter of which is something
          Apple would certainly never allow (their on-device SQLite build
          explicitly disables registering extensions). So the global registry
          would largely be for storage purposes.
        </p><p>
          The next question would be one of security, both in guarding which
          apps are allowed to access certain models, and in terms of defining
          “what is a model”.
        </p><p>
          Open weight models like are probably ok to share globally with other
          apps, but this wouldn’t be ideal for proprietary models/fine-tunes.
          Today, we have privacy by default since each app has its own sandbox.
          iOS can support limited sharing through app-group containers, and
          Android also has mechanisms for this.
        </p><p>
          Next is “what is a model”? Presumably, the registry would be optimized
          for model storage specifically, but models are merely distributed as
          ordinary files with some engines having their own dedicated file
          formats. In essence, this would largely be a standard directory
          service that an app would probably interact with through an on-device
          framework (eg. <code>ModelStorageKit</code>), but even this framework
          would likely have to resort to depending on a plain <code>URL</code>
          from both Swift and Kotlin respectively. Various app review processes
          would be necessary to determine if the registry is being used for
          “model files” in practice.
        </p><p>
          Afterwards, we want to allow end-user control somehow. Of course, 99%
          of users are completely clueless as to how any of this works, and thus
          won’t be able to exert much control in the first place. (See Apple’s
          “Buy more Intelligence” messaging for requiring users to upgrade their
          iCloud accounts for more Private Cloud Compute access…) Generally
          speaking, I’m unlikely to come up with something good here since I’m
          primarily looking at this from a technical lens first.
        </p><p>
          All in all, this is clearly a very silly idea. Though I still do think
          the problem may become more real if edge inference picks up more, so
          hopefully by that point we have better means of solving this. The
          end-user perspective is something that will need to be fleshed out
          more because I don’t think “here’s a screen with all the model names
          and the ability to delete them” is a good UI for this. Furthermore,
          it’s also clear that the current means of model distribution don’t fit
          nicely into this potential system, and I suspect more than just simple
          shared file storage would be necessary.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>What Would it Take to get Rid of the Code? — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#what-would-it-take-to-get-rid-of-the-code</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#what-would-it-take-to-get-rid-of-the-code</guid>
      <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          The biggest reason why I still think today’s code (both reading and
          writing manually at the very least from time to time) is necessary is
          simply to boost understanding of what agents are doing. Therefore, if
          you want to get rid of it entirely without causing catastrophic
          destruction, you need a substitute that provides similar or greater
          levels of systems understanding than it. For the record, not even
          compilers have solved this 100% because viewing (or even writing in
          the case of FFMPEG, Linux Kernel, ONNX, etc.) assembly is still useful
          in some contexts.
        </p><p>
          Despite that, I think there are many ways to achieve greater
          understanding without today’s code, but it differs from domain to
          domain (eg. UIs and ML Kernels have different needs and programming
          styles). The end result will still be and feel like programming, but
          with different representations more tailored to the problem domain.
          Ideally ones that are more aligned with the advanced thinking styles
          of the problem domain. (eg. Geometric representations for UIs, more
          mathematical/information oriented representations for ML models, etc.)
        </p><p>
          However, I don’t think today’s code as an art form will disappear. A
          lot of us pre-agents were doing this because of enjoyment for the art
          form, that generally doesn’t just go away all of a sudden.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>The Need for Local Models — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#the-need-for-local-models</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#the-need-for-local-models</guid>
      <pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          It’s true that most consumers aren’t and will likely not be capable of
          running models like GLM 5.2 in the near furture. Further, the frontier
          seems to be expanding further with models like Fable 5 and GPT 5.6
          Sol, meaning that models like GLM 5.2 are technically a tier below the
          true frontier. It’s also true that more and more workloads are
          parallel these days, which means just having the hardware to run a
          single GLM 5.2 instance isn’t enough on its own. With all that said,
          local models “can be a future”, if we mean a future that includes a
          medium only possible through local models. That is, they serve a
          different purpose than the frontier, and one that the frontier cannot
          fulfill itself.
        </p><p>
          Inference requires more hardware resources than gaming, especially so
          for memory. To put this into perspective, most AAA games can played
          casually on a single NVIDIA GPU or directly on consoles that cost
          &lt;$1000. Yet, running just a single inference task on a frontier
          model requires a cluster of enterprise-grade GPUs and parallel
          distributed overhead due to available memory bandwidth. Training
          requires even more resources than that generally. So the compute needs
          for SOTA inference are often greater than SOTA gaming, and we’ve tried
          to put gaming in the cloud. It’s generally a worse experience due to
          latency.
        </p><p>
          This is to say that no amount of compute can remove the constraint of
          a simple network connection, which means that a truly realtime medium
          is likely best suited for local models. For the record, this doesn’t
          necessarily mean LLMs either. In fact, if anything it’s more likely
          that LLMs are <em>not</em> the kind of model for the kinds of mediums
          that would benefit most from local models (see
          <a href="https://en.wikipedia.org/wiki/World_model_%28artificial_intelligence%29">World Models</a>).
        </p><p>
          Of course, I’m also ignoring the need for privacy, ethics, etc. Those
          are also things that necessitate local models, but I don’t consider
          those the selling point because historically few in the general public
          care about those concerns. (If they did, they wouldn’t be using social
          media the way they currently do.) The medium is the massage (the typo
          is intentional if you're familiar with McLuhan), and that’s what I
          would rather focus on. Especially given that any medium driven by
          local models will also have privacy and ethics concerns more tightly
          integrated by default.
        </p><p>
          In essence, I would avoid looking at local models as a “cheaper and
          more private/ethical alternative” to frontier models. The frontier
          will likely always have an edge on local models for “hands-off”
          non-latency critical agentic workflows, which is the major paradigm
          today. However, AI is thankfully more vast than agentic workflows and
          LLMs. Almost certainly there’s at least something that can come out of
          that where local models will have an edge over the cloud.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Swift WASM Bundle Sizes With Foundation — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#swift-wasm-bundle-sizes-with-foundation</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#swift-wasm-bundle-sizes-with-foundation</guid>
      <pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I asked GPT 5.6 to run a simple experiment on a basic executable Swift
          package that imports Foundation, FoundationEssentials, YYJSON, and no
          Foundation import to compare the bundle sizes. It came out with this.
        </p><p><b>Original Prompt:</b></p><pre class="code-wrap"><code>I want you to run a quick experiment.
 - Create a very simple Swift package that imports Foundation, and compile it for WASM.
 - Then change the Foundation import to FoundationEssentials, and compile it for WASM.
 - Then remove the import entirely, and compile it for WASM.

 At the end, report back the WASM binary sizes for each build. Make sure to build in release mode for each build.</code></pre><p><b>Later Added:</b></p><pre class="code-wrap"><code>Ok, now run the same experiment, but add another build that imports a C target exporting and using yyjson. Compare the new binary size to the other cases (you don't have to run the other cases again, just compare the new case to the ones you have here.)</code></pre><p><b>Final Result:</b></p><pre class="code-wrap"><code>┌──────────────────────┬──────────────────┬──────────────────┐
│ Variant              │ Pruned WASM      │ Brotli-11        │
├──────────────────────┼──────────────────┼──────────────────┤
│ Foundation           │ 45,718,166 bytes │ 12,016,398 bytes │
├──────────────────────┼──────────────────┼──────────────────┤
│ FoundationEssentials │ 7,765,629 bytes  │ 2,011,377 bytes  │
├──────────────────────┼──────────────────┼──────────────────┤
│ No import            │ 3,319,821 bytes  │ 896,977 bytes    │
├──────────────────────┼──────────────────┼──────────────────┤
│ CYYJSON / yyjson     │ 3,667,654 bytes  │ 984,086 bytes    │
└──────────────────────┴──────────────────┴──────────────────┘

 The yyjson case is:
 - +347,833 bytes over no import
 - 4,097,975 bytes smaller than FoundationEssentials
 - 42,050,512 bytes smaller than Foundation</code></pre><p>
          Suffice to say, 12MB shipped to the browser just because of Foundation
          is not ideal at all considering most web-apps are a fraction of the
          size. FoundationEssentials cuts down on this, but it still more than
          doubles the size of this simple package. Goodnotes’
          <a href="https://www.swift.org/blog/bringing-goodnotes-to-web-with-swift/#binary-size-and-loading">WASM binary size</a>
          is ~50MB, but it was able to be compressed down to ~12MB, and I am
          very certain they import Foundation.
        </p><p>
          Often, I find myself using importing Foundation in library code
          because I just need <code>JSONEncoder</code>,
          <code>JSONDecoder</code>, <code>URL</code>, or <code>Date</code>.
          However, as we can see this increases the bundle size by a lot for
          WASM.
        </p><p>
          <code>URL</code> and <code>Date</code> can be solved by using
          <code>String</code> and <code>Double</code> for most cases
          respectively. However, I would still provide a package trait for
          Foundation specifically such that the library can still ship APIs with
          them.
        </p><p>
          <code>JSONEncoder</code> and <code>JSONDecoder</code> on the other
          hand are more painful because building a good JSON parser is not
          trivial, and is out of the scope of most projects. However, we can see
          that vendoring a small, portable, and fast C library like
          <a href="https://github.com/ibireme/yyjson">yyjson</a> can solve a lot
          of that issue. The only problem is in order to support Codable types,
          its up to you to make your own equivalents of
          <code>JSONEncoder</code> and <code>JSONDecoder</code> that don’t also
          import Foundation if you want to keep the binary size small. Though,
          it shouldn’t be too hard or tedious to do this with the help of an
          agent, as it is mainly a simple Swift wrapper.
        </p><p>
          That being said, it would be ideal to see some of these trivial types
          in the standard library instead of Foundation, primarily to avoid
          awkward cases like this.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>I Like Pi — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#i-like-pi</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#i-like-pi</guid>
      <pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I've been using
          <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>π</mi></mrow><annotation encoding="application/x-tex">\pi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height: 0.43056em; vertical-align: 0em"></span><span style="margin-right: 0.03588em" class="mord mathnormal">π</span></span></span></span>
          for the last few days, I like it much better than opencode so far. I
          also think it captures some of the ideas that I think are needed
          (simple by default, highly extensible by end-users, etc.).
        </p><p>One question is how can we do this on a larger scale?</p><br>]]></content:encoded>
    </item>
    <item>
      <title>The Need for a Simple Conceptual Model for ML/AI — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#the-need-for-a-simple-conceptual-model-for-ml-ai</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#the-need-for-a-simple-conceptual-model-for-ml-ai</guid>
      <pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Don Norman's idea of conceptual models is quite important for
          designing any tool used by humans, and
          <em>The Design of Everyday Things</em> shows many examples of getting
          such models wrong for even simple objects (especially doors if you
          read the book). That being said, ML/AI and many of the
          sciences/engineering projects offend this idea quite a lot, because
          they are complex to the point where lots of pre-requisite knowledge is
          required if you look at them purely symbolically.
        </p><p>In ML, we can state a very simple conceptual model could be:</p><pre><code>x -&gt; f(x) -&gt; y

Where:
x = Some input
f = Some model
y = Predicted output from f</code></pre><p>
          For those with a mathematical background this makes intuitive sense,
          but for those who despise math (ie. most of the population), this is
          complete gibberish. However, even for those with a mathematical
          background, there's still the question of: What does
          <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow><annotation encoding="application/x-tex">f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="
                    height: 0.8888799999999999em;
                    vertical-align: -0.19444em;
                  "></span><span style="margin-right: 0.10764em" class="mord mathnormal">f</span></span></span></span>
          look like exactly? Of course, there is some outer observable behavior
          we can sample statistically, but the uncertainty of this forces us to
          use statistics. Still, at least those with a mathematical background
          are able to reason with uncertainty provided by a model.
        </p><p>
          What about everyone else however? Who don't have the ability to think
          this way, and who must vote on policies in this area? First, they get
          media headlines, which have clear ulterior motives alongside often
          being written by those who are also in the same boat. Secondly,
          without a conceptual model of ML, all they can rely on is guessing its
          tradjectory superstitiously. Talk to any non-technical person about
          AI, you'll nearly always run into the "replacing all the jobs" topic,
          and nowadays you may hear their opinion about all the datacenters.
          Thirdly, their point of view of AI is that it's exactly what ChatGPT
          is, which is a limited point of view of the entire field.
        </p><p>
          There's another class of people who write code professionally, but run
          as soon as they see a math formula. Many of these people will be
          working on agentic systems in the future, so some level of
          understanding is essential. While these people are generally far
          better off than the average joe, experience shows that many also don't
          get far past the brief
          <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y = f(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height: 0.625em; vertical-align: -0.19444em"></span><span style="margin-right: 0.03588em" class="mord mathnormal">y</span><span class="mspace" style="margin-right: 0.2777777777777778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right: 0.2777777777777778em"></span></span><span class="base"><span class="strut" style="height: 1em; vertical-align: -0.25em"></span><span style="margin-right: 0.10764em" class="mord mathnormal">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span>
          conceptual model I presented at the beginning. (eg. Many Claude
          Code/Codex users don't know how KV caching works, even though it's
          directly relavent to token costs. Perhaps they will take note of this
          when the subsidization isn't as generous!) Over time, the standard
          "technical interview curriculum" may screen for basic regurgitation of
          how LLMs work under the hood, but regurgitation isn't understanding.
        </p><p>
          As a result, fear is the most justifiable reaction for many. There is
          no way for them to reason about uncertainty, and what their told isn't
          exactly encouraging either. They need an
          <a href="https://worrydream.com/refs/Kay_2005_-_Enlightened_Imagination_for_Citizens.html">enlightened imagination</a>, which starts from a good set of conceptual models on an individual
          level. The larger scale effort for "achieving this in everyone"
          requires much more, and is outside the scope of this note, but
          universal basic literacy shows that such a thing can likely be done.
        </p><p>
          So what does a good conceptual model look like? This is something I'm
          still trying to figure out and finalize, but I can at least share
          something that has thus far worked quite well for myself. The model
          I've thus far converged on is to think of a model as an
          <em>"executive process of knowledge"</em>. That is, with each
          operation, layer, representation, etc. what semantic "meaning" does it
          convey?
        </p><p>
          For example, full-attention is
          <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height: 1.064108em; vertical-align: -0.25em"></span><span style="margin-right: 0.02778em" class="mord mathnormal">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height: 0.8141079999999999em"><span class="" style="top: -3.063em; margin-right: 0.05em"><span class="pstrut" style="height: 2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>
          , which makes it scale poorly when the context grows linearly, but it
          turns out that not every token needs to attend to every other token in
          the context. Imagine a book, it's unlikely that a random paragraph
          from chapter 6 is directly relevant to a random paragraph in chapter
          2, but the words within the paragraph of chapter 6 are highly
          correlated with each other. We can use this intuition to begin to see
          how we may optimize attention/context/KV caching to support things
          like longer context windows, resource constrained environments, etc.
        </p><p>
          Now a layman wouldn't understand any of the ML or CS specifc terms
          that I mentioned in the last paragraph, but they have a much better
          chance of understanding this sentence.
        </p><blockquote>
          <p>
            Imagine a book, it's unlikely that a random paragraph from chapter 6
            is directly relevant to a random paragraph in chapter 2, but the
            words within the paragraph of chapter 6 are highly correlated with
            each other.
          </p>
        </blockquote><p>
          Is this a gross simplification on many different levels? Absolutely,
          but it does show the beginnings of how one may "reconstruct" the
          complexity to a more learnable form. This idea of reconstructing can
          be applied all the way down to the implementation details of
          techniques that segment the context, KV cache, etc. That is, for each
          concept in the implementation, there is a way of reconstructing its
          ideas in a form understandable for a given individual. This is already
          what practitioners/researchers are already doing, even if
          unconsiously.
        </p><p>
          Another issue I see is the prevelance of ML frameworks and libraries
          that hide much of the complexity. (ie. Violating Butler Lampson's
          "don't hide power" principle.) For instance, Apple's FoundationModels
          framework hides many of the details of the underlying language model,
          and further is only designed for multi-modal (text-only pre-Apple-OS
          27 versions) LLMs, but otherwise has an intuitive API. Hiding the
          complexity in this way presents 2 problems for uninformed users.
        </p><ul>
          <li>
            One doesn't realize they have more control than they think over a
            model.
          </li>
          <li>
            One begins to think that all models take the same shape as whatever
            a framework allows.
          </li>
        </ul><p>
          To conclude, this note has one primary point:
          <em>universal ignorance isn’t bliss for a foundational technology</em>.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>2 Paths from Agents — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#2-paths-from-agents</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#2-paths-from-agents</guid>
      <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Agents are largely swallowing the need to handwrite typical "glue
          libraries together" kind of application code. This code was never hard
          to write in the first place, a lot of it was tedious, and the true
          hard part was in the thinking/architecting stage. Intent is overall
          easier to communicate to an agent than precision (which is the entire
          point).
        </p><p>
          One obvious path from this is to fully lean into agents, embrace
          loops, product engineering, etc. to produce new software in a
          factory-like style (or whatever mainstream style comes in the future).
          This will likely be the most common path, because it's the most
          natural step up for most engineers building traditional
          mobile/fullstack apps.
        </p><p>
          The other is to see that agents can write traditionally tedious code,
          and lean into the fact that you have time to do something harder now
          (ie. Something agents can't replicate with ease). For example, while
          still incredibly useful tools, agents don't replace the need for
          research, have difficulty with performance sensitive/security critical
          code, can't build hardware, and generally don't train ML models. This
          path requires a lot of learning, potentially more than embracing
          agents because it isn't a natural next step for typical application
          devs. As a result, it will likely be less common, but may be
          worthwhile for those who feel that agents have taken away the art of
          coding from them.
        </p><p>
          That being said, regardless of the path, agentic systems are where
          things are (currently) headed. Therefore, one does need to learn more
          beyond just being a mere consumer of coding agents regardless of the
          path.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>How Spiritual Optimism Occurs (And How to Counter It) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#how-spiritual-optimism-occurs-and-how-to-counter-it</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#how-spiritual-optimism-occurs-and-how-to-counter-it</guid>
      <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>Ok, another philosophical note. (You’re welcome.)</p><p>
          As said in the previous note, spiritual optimism is a general trait of
          superstition, which is a human universal form of thinking. Generally,
          I think deep styles of thinking (engineering, mathematical,
          scientific, physics, creative, etc.) help get around this, but this is
          very hard for people without those backgrounds (which is unfortunately
          most people, who also are likely not the ones reading this). The
          reason I say this is because a style of deep thinking often gives one
          a framework on how to approach evident situations, and such a
          framework is often reasonably (or perhaps
          <a href="https://worrydream.com/refs/Hamming_1980_-_The_Unreasonable_Effectiveness_of_Mathematics.pdf">unreasonably</a>) effective in dealing with those situations.
        </p><p>
          The effectiveness of the framework is the key to countering
          superstition, because the thought patterns that lead to superstition
          generally aren’t very effective (they wouldn’t be superstitious
          otherwise!). Despite that, even people who have backgrounds in the
          kinds of deep thinking styles I’m referring to still commonly fall for
          superstitions, especially for cases where there may be a mismatch
          between the situation and their familiar thinking styles. In this
          sense, I believe there are still more styles of thinking that need to
          be discovered that have not been discovered yet. Most likely, such
          future thinking styles will be encoded as entirely new fields or
          industries that don’t exist today.
        </p><p>
          In ~200,000 years of human history, geometry found its roots circa
          70,000 BC, and engineering only really started appearing in 10,000 BC.
          The notion of “science” took a lot longer to appear, only becoming a
          widespread phenomenon starting in the 16th century. It took another 2
          centuries to realize that absolute monarchy wasn’t a good idea to the
          point where it could be replaced with newer democratic systems. At the
          same time, the Industrial Revolutions developed the modern notions of
          work, public education, society, etc. Computing, electricity,
          universal literacy, and the internet only became widespread last
          century, and it’s taken until the present time for modern AI to become
          widespread. Each of these points spawned new fields, industries, and
          subsequent styles of thinking to make the fields and industries work
          in the real world.
        </p><p>
          We’re still very adolescent when it comes to developing such methods
          of thinking, which is also why I don’t think of “human-level
          intelligence” as a viable end-state for AI (the target is always
          moving, and the current target has many flaws).
          <a href="https://qr.ae/pFHNHk">“Biology and beyond” is where computing needs to be, so this is a
            good way to gauge where a program/system is.</a>
        </p><p>
          Regardless, the overall point is that to counter Spiritual Optimism,
          you actually need to understand how things work. When you don't
          understand something, you need the inclination to learn rather than
          depend on superstition. New styles of thinking help with that, but
          it's also worth asking how we can cultivate more of the inclination to
          understand things. Either way, superstition is a human universal, so I
          still hold the point that any system designed for humans needs to take
          it into account.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Spiritual Optimism — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#spiritual-optimism</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#spiritual-optimism</guid>
      <pubDate>Sun, 21 Jun 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’ve been talking about this term I’ve made up to a few people
          recently, so I’ll figure that I’ll define it here in a centralized
          place since there was interest.
        </p><p>
          Quite simply, it’s formal definition is:
          <em>A large amount of insistence that performing a certain action once
            or repeatedly will produce a specified outcome that cannot be
            universally proven to be solely attributed to the action.</em>
        </p><p>
          Blindly using AI is currently a great example of this. However, this
          also commonly exists in tech decisions, such as assuming that using
          Rust will solve all of your security problems, or that applying
          certain design patterns will make your codebase better.
        </p><p>
          So what happens when performing the action doesn’t work, or even
          produces the opposite of the intended result? One of the most common
          rhetorics is to tell the actor that they are simply performing the
          activity wrong (skill issue) or not believing hard enough in the
          “doing” of the action (disbeliever). While this may be true in any
          given scenario, it often misses critical parts of doing proper outcome
          analysis, but often the worst part of this is that it
          <em>discourages</em> finding or doing the necessary critical parts
          during analysis. Furthermore, it may cause more advancements in the
          doing of the problematic action, which is often good, but can make
          things worse culturally if such advancements don’t cover the case for
          why the desired outcome wasn’t achieved.
        </p><p>
          Of course, this principle goes further than software because it’s
          really an explanation of the general human trait of superstition. This
          kind of thinking has actually played a part in civilizational collapse
          before, one case is the Black Death in 1340s/50s which killed an
          estimated 50% of the European population. In that case, a relatively
          common belief at the time was that those affected by the plague were
          sinners of religious faith being dealt divine punishment by God.
          Furthermore, medieval medecine was based on balancing the 4 humors
          (blood, yellow bile, black bile, phelgm), and further shaped by
          miasma, astrology, and regimen.
        </p><p>
          Needless to say, these approaches were spiritually optimistic in the
          sense that religious faith would always bring safety during hard
          times, and on the fact that existing medicine was the key to good
          health. However, the spread of more religious faith and improvements
          in humor balancing, though pushed for, weren’t what directly helped
          curb the plague. Rather, it took newer/modern practical public-health
          measures like quaratines to stop much of the spread.
        </p><p>
          I can keep extrapolating, but I think the point is clear. What is true
          however is that this is a clear problem that must be taken into
          account when it comes to systems design, and especially so for usage
          patterns of AI (see cases of psychosis, extreme subsidization, etc.).
          That is, we cannot prentend like users are logical people who don’t
          believe in absolutely insane things (we all do).
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Open Weight Models — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#open-weight-models</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#open-weight-models</guid>
      <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’m finding open weight models to be more capable in recent times.
          What’s also cool about them is that the labs behind them actually
          <a href="https://arxiv.org/pdf/2606.13392">publish</a> architectural
          details of their research. I still primarily work with GPT 5.5, which
          does significantly outperform the open weights on most tasks, but on
          side projects I’ve found that Minimax M3 can handle many of the same
          tasks as GPT 5.5. That being said, I generally think this mostly
          applies because most necessary code changes are smaller and more
          iterative. For longer running work or harder tasks, I would still pick
          GPT 5.5.
        </p><p>
          However, the main cause for using coding agents in the first place was
          the big inflection point in December/January. Once a model gets to
          that point in terms of performance, it becomes viable so to speak, and
          improvements afterwards are mostly incremental in the sense that you
          can do larger amounts of work with them with less intervention. That
          is, you can still get lots of work done with older models like GPT
          5.2, GPT 5.3 Codex, and Opus 4.5/4.6 if you want to, but they will
          require more steering. The bigger increases in productivity since that
          time seem to be more tool/harness-based as we understand use cases
          better, and offer more ways for agents to close the loop for a given
          task.
        </p><p>
          As for Minimax M3 specifically, it does tend to get “stuck” more
          often. I had to fight with it for an hour for it to realize that it
          should be writing the string “tool_call” rather than “tool çcall”,
          which was one of the only few agent conversations where I prompted the
          agent in all caps to indicate frustration (I literally told it to use
          the proper string over and over again, and it just could not put 2 and
          2 together…). Eventually, I had to handwrite code like a caveman to
          solve the issue. All jokes aside, I still do handwrite and enjoy
          handwriting code, especially so for library/framework code that
          requires stability (though we’ll see if the future dictates whether or
          not this will remain an option). Though overall, I find that the model
          does fine with appropriate steering, just like any other model, and
          that many of my issues probably arise from the fact that I’m primarily
          a GPT 5.5 user.
        </p><p>
          On a side-note, I hear many people like to swear to their agents, and
          genuinely use very “controlling” (to put it nicely) language to them.
          In my case, if it weren’t for the fact that such conversations
          contained work details, I wouldn’t mind sharing them with others. I
          hope this dynamic doesn’t worsen our overall social development given
          that most working conditions are already bad enough as is lol. (It
          absolutely is having some psychological effects that aren’t good, but
          those are topics for another time.)
        </p><p><b>Update (6/21/26):</b> I have since been enjoying GLM 5.2.</p><br>]]></content:encoded>
    </item>
    <item>
      <title>Some (Preliminary) WWDC Thoughts — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#some-preliminary-wwdc-thoughts</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#some-preliminary-wwdc-thoughts</guid>
      <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Since it’s that time of the year again, and after spending a good
          amount of the week in Cupertino, I suppose I can write some things.
          I’ll mainly focus on the actual dev stuff like new frameworks, APIs,
          Swift language updates, etc. since I’m sure you can find better
          reviews about the new OS versions from other sources. Though, I’m
          still going through many of the video sessions, which is a task that
          I’ll expect will take the entire weekend, and some time after to
          actually use the new tools in various projects.
        </p><p>
          I can actually recommend FoundationModels as a usable framework now,
          primarily because it’s possible to integrate other engines with it. I
          also like the general API shape they took (it seems similar to
          <a href="https://github.com/mhayes853/swift-cactus/pull/38">this</a>
          agent framework that I had in mind at one point, but adandoned because
          it wasn’t worth scoping such a general framework to a single inference
          engine). Furthermore, one issue in Swift was the lack of a standard
          generic framework for writing agentic workflows, which caused me to
          write an entire agentic API from scratch for Swift Cactus that was
          similar, but not the same as the FoundationModels API. Given that
          Apple is opening up the core of FoundationModels and getting Anthropic
          + Google to contribute official adapter packages, I think it’s safe to
          say that we have that standard now. All of this means that engines can
          focus on being good engines now without having to worry much about
          creating a good Swift API, which isn’t the kind of work that most who
          work on such engines are suited for.
        </p><p>
          CoreAI seems like a cool CoreML successor. However, it faces the
          typical Apple platforms problem of being scoped to only the latest OS
          version, which means that it will be at least 2 more years until it
          can be used reliably at a widespread scale. In the meantime, there are
          <a href="https://github.com/ml-explore/mlx-swift-lm">quite</a>
          <a href="https://github.com/pytorch/executorch">a</a>
          <a href="https://developer.apple.com/documentation/coreml">few</a>
          <a href="https://github.com/cactus-compute/cactus">more</a>
          <a href="https://github.com/ggml-org/llama.cpp">options</a>
          of which most also work (or will soon) with any torch model.
        </p><p>
          SwiftUI seems to have stabilized quite a bit given the relatively
          simple additions this year. Notably, I would say it’s nice to see more
          robust document support (so one doesn’t have to fallback to using
          <code>UIDocument</code> or <code>NSDocument</code> as much). Though,
          very advanced text capabilities outside of cool animations are still
          seem to require dropping down to UIKit/AppKit/TextKit. Nevertheless, I
          think agents have largely made writing traditional UI code quite
          straightforward no matter the framework, so dropping down to
          UIKit/AppKit is easier than ever if SwiftUI is missing something you
          need.
        </p><p>
          Swift Data also finally allows observations outside of SwiftUI views,
          but once again this faces the OS requirement problem. Even then, I’ve
          never been biggest fan of Swift Data due it feeling like a black box.
          <a href="https://github.com/pointfreeco/sqlite-data">SQLiteData</a>
          supports the full capabilities of SQLite, is open source, uses Swift
          value types, supports Observation, works outside the Apple ecosystem,
          and even has automatic CloudKit sync if you desire it (Though
          personally, I’ve found that application-specific sync engines make
          more sense for large complex things). That being said, value types and
          the Observation can be a rabbit hole, especially when it comes to
          observing individual properties on larger value types (I even once
          made
          <a href="https://github.com/mhayes853/WhyPeopleKit/blob/main/Sources/WPPerception/ObservedValue.swift">this helper class</a>
          to make such observation easier).
        </p><p>
          <code>withContinuousObservation</code> is also a nice addition to the
          Observation framework, but has the OS requirement problem.
          <code>observe</code> from
          <a href="https://github.com/pointfreeco/swift-navigation/blob/main/Sources/SwiftNavigation/Observe.swift">Swift Navigation</a>
          provides most of the same functionallity, but will work properly on
          any Observation supported OS version. It’s also quite self-contained
          so you can drag and drop into your project if you don’t want the
          dependency.
        </p><p>
          I personally try to avoid Xcode in favor of using Zed and the Codex
          desktop app, and also tend to write most of my code (even for apps) in
          Swift packages. Therefore, I cannot comment much on its updates.
          Primarily this is because of performance, but also the fact that I
          work with other languages, so it’s nice to have a single editor for
          all of my code. That being said, there are more alternatives to Xcode
          than ever before, so you can always use something different if it
          doesn’t live up to standards.
        </p><p>
          As for video sessions, many felt more like guidance/how you should do
          things kind of sessions over “here’s some example code” like in
          previous years. This makes sense given how agents are handling most of
          the code writing now, which means more emphasis is placed on
          higher-level patterns. (That being said, the need for great
          engineering is needed more than ever now, and that generally doesn’t
          come from looking at a few code examples.)
        </p><p>
          Overall, I would rate this year higher than last year where much of
          the focus was on some glass (see
          <a href="https://whypeople.xyz/liquid-glass">this</a>). Though I
          suppose it’s annoying that my Apple Watch Series 7 will not be getting
          watchOS 27, time to upgrade…
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>The Problem With AI Terminals — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#the-problem-with-ai-terminals</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#the-problem-with-ai-terminals</guid>
      <pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Alan's answer to this
          <a href="https://www.quora.com/Can-an-AI-command-line-replace-the-GUI-as-the-primary-user-experience-for-computers-assuming-the-technology-improves-and-irrespective-of-today-s-state">question</a>
          puts more light on what I think regarding general chat interfaces, and
          especially interfaces to agentic tools. One of the main issues being
          the complete lack of visibility, feedback, and also as Alan says,
          exploration (through all senses, which really can be a superset of the
          "visibility" and "feedback" traits). Without such traits, the
          effectiveness of these tools (in their existing forms, irrespective of
          how good the models are) tends to depend on the existing domain
          capabilities of the user, which can be a negative multiplier for more
          incapable users.
        </p><p>
          In the long term, I don't see this particularly leading to mass good
          outcomes, but rather many bad ones with a few possible good ones from
          necessarily small teams of "responsible" users. This is already
          <a href="https://www.businessinsider.com/uber-coo-andrew-macdonald-ai-token-spending-harder-justify-2026-5">happening</a>
          at organizations that have the means to capture even the best talent.
          (I also think much of this relates to "maintenance" vs "creation"
          mode, of which the former tends to apply more to large companies like
          Uber. In practice, maintenance is more about hardening and making a
          system more robust, which is quite different from writing new
          functionallity from scratch. That is, one cannot fix systems with the
          same mindset used to create them.)
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Meaning Per Second (2/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#meaning-per-second-2-n</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#meaning-per-second-2-n</guid>
      <pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          This
          <a href="https://mikeveerman.github.io/tokenspeed/?rate=76.5&amp;mode=text">site</a>
          shows various token decode speeds visually from an "average" (they all
          differ, but those differences don't matter for this) BPE-style
          tokenizer. Try playing around with the speeds a bit.
        </p><p>
          What becomes strikingly clear is that at a certain point the
          bottleneck becomes your eyes if you watch the stream visually, and you
          cannot process text being decoded that quickly (also notice that your
          eyes will try to "fill in the gaps" when line breaks for paragraphs
          are decoded). In that sense, the rate of visual streaming should not
          reasonably be the same as the decode speed in application code. So
          when it comes to communicating meaning in a visual throughput context,
          the ratio of meaning to throughput matters much more than the raw
          throughput and raw content separately.
        </p><p>
          A lot of this can also be foreshadowed as to what an ideal
          representation would be. That is, a representation that conveys visual
          meaning, that can be incrementally expanded and explored in real-time,
          and can be formed reasonably quickly. Throwing LLMs at this problem
          isn't the full answer. Something different is needed, especially for
          edge use cases.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Language Fungibility — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#language-fungibility</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#language-fungibility</guid>
      <pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Programming languages are becoming more like components of the system
          these days rather than the life-blood of a system. Further, my
          previous note goes over how to learn them with great efficiency, and
          addresses many of the common arguments against giving new learners
          access to agents.
        </p><p>
          Given that Bun was recently ported from Zig to Rust in a matter of
          weeks, it's more clear now than ever that language choice is becoming
          much less of a decision that locks the life-blood of your project. My
          experience also shows this, where I've been able to write a module in
          one language, and then tell an agent to create a version of the module
          in another. So far, this kind of transpilation hasn't failed me, and
          if it can work on something performance sensitive like Bun, then it
          can work for traditional libraries as well.
        </p><p>
          I've also had it work for other things like adding new formats to my
          <a href="https://github.com/mhayes853/swift-stream-parsing">stream parsing library</a>
          where I had an agent derive the YAML parser by looking at the
          implementation of the JSON parser. It turns out, the mechanics of
          parsing were very similar minus the obvious syntax differences between
          YAML and JSON. In fact, earlier agent attempts would even transpile
          the YAML to JSON, and then call the JSON parser!
        </p><p>
          That being said, this transpilation technique only works if you've
          figured out the architecture and design quirks of your system. I
          wouldn't pair a transpilation task with re-architecting using an
          agent. This is a setup for a lot of pain.
        </p><p>
          Something I would like to see however is what would happen if you
          asked the agent to translate Bun's new Rust port back into Zig?
          Intuition tells me that this is likely a lossy compression algorithm,
          and that the original Zig code would be better of the the transpiled
          Zig code. However, there's no way to definitively prove that case.
        </p><p>
          <em>This note, without mentioning anyone in particular, may or may not
            be addressed at a certain group of individuals who have claimed that
            they will rewrite their project in Rust, but have not yet done so.
            It is an exercise left to the reader to determine that group of
            individuals, and to motivate them to do so.</em>
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>How to Learn Languages With LLMs — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#how-to-learn-languages-with-llms</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#how-to-learn-languages-with-llms</guid>
      <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Ever since the agent boom, this has been a topic much discussed, and
          particularly for beginners that have tendencies to jump right into
          agentic coding without first handwriting code. However, I don't think
          there's anything really new here in terms of the advice I would give
          to new learners, especially since this story has played out before
          with beginners learning React before Vanilla JavaScript, SwiftUI
          before Swift, etc. This is going to be a more contrarian take on
          learning programming, and I believe the consensus take comes from a
          place of desiring control in times of uncertainty.
        </p><p>
          I disagree with the general sentiment that states that one must ascend
          through the "fundamentals" before they get to use the cool frameworks
          or techniques that are perhaps more abstracted from the
          "fundamentals". It is true that pure vibe coders and framework
          warriors aren't the ideal end-states for new programmers. However,
          I've known many others in the past who were said framework warriors
          that eventually learned the "fundamentals", and are sufficiently
          skilled today. (eg. In the Swift world, Paul Hudson begins 100 days of
          SwiftUI with only 2 weeks of Swift fundamentals, and every subsequent
          project is building apps with SwiftUI and other Apple frameworks. Many
          talented developers today went through that course as their intro to
          programming in general.)
        </p><p>
          What we call the "fundamentals", and the designs of various frameworks
          and agentic coding styles are really all just <em>ideas</em>. Ideas
          are things that we play with through exploration, intuition,
          connection, etc. Without this, we would never discover new insights or
          novelty. It's worth noting that most traditional schooling or
          structured curriculum typically does not promote learning via playing
          with ideas, but rather through drilling them into you in a specific
          order via fear and punishment. This often fails to cultivate any
          enthusiasm for long-term learning other than for personal extrinsic
          achievement.
        </p><p>
          So really, if a learner isn't interested in playing with ideas, they
          aren't going to be good regardless of whether or not they start with
          the "fundamentals" or with the bleeding edge. If they start with the
          fundamentals, they'll quickly become disinterested because they'll get
          bored. If they start with the bleeding edge, they'll never reach a
          sufficient level of competency because they have no reason to do so
          (the bleeding edge abstracts them).
        </p><p>
          By contrast, a person who loves playing with ideas will do fine no
          matter what path they start with. If they start with the
          "fundamentals", they'll enjoy playing with the foundational ideas, and
          become curious as to how those apply to the bleeding edge. If they
          start with the bleeding edge, they'll become curious as to how the
          fundamentals will improve their usage of the bleeding edge stuff.
        </p><p>
          This means the more important goal here should be to cultivate
          learners who love playing with ideas, especially those of "systems"
          and "processes". There are many ways to do this, but I don't think a
          strictly structured curriculum is the answer you want here. Good
          "systems" and "processes" often take many ideas from the "outside", so
          you need to allow for this exploration to happen (which often requires
          removing structure in favor of creating a good overall environment).
        </p><p>
          Some people are concerned about giving "juniors" access to agentic
          tools, and that they should earn their right to use them. However, I
          think it is wrong to deny learners access to essential tools that they
          will likely have to use everyday in the future. Richard Hamming once
          said that teachers should prepare their students for their future, and
          not the teacher's past. We can claim the quality of agent code is bad
          or otherwise lacking (it certainly is severely lacking compared to a
          good human). However, bad code won't stop agents from becoming a
          necessary everyday industry tool (this point comes from Casey
          Muratori).
        </p><p>
          A corollary example to this is with regards to the skill of general
          communication, of which most people are seemingly lacking. If the
          training for the workplace was the school classroom, then we'll take
          note that "talking" is often forbidden in the classroom, and that it's
          a right to be earned. That is, in order to earn your right to begin
          building your oral communication skills, you must first earn the right
          to talk in the first place. Of course, you would think that the last
          sentence is ridiculous when stated as such, yet we enforce it in
          schools anyways.
        </p><p>
          <em>Forcing people to "earn" the right to learn essential skills is
            generally a great way to gate-keep people from learning them.</em>
        </p><p>
          So I say let new learners use the agents in safe environments. As long
          as they are interested in playing with ideas, they <em>will</em> make
          an effort to avoid the negative effects of cognitive outsourcing
          present in today's tools. Of course, future tools could offer more
          forms of input and visibility which can massively help with reducing
          that outsourcing, and may even <em>enhance</em>
          understanding. Despite that, today agents are an output amplifier of
          current thinking. A curious person using them will likely remain
          curious, and a non-curious person using them will likely remain
          non-curious.
        </p><p>
          This isn't an AI problem, it's a cultural learning problem in general,
          so any long-term fix would need to happen at that level. Good tools
          can aid that process, but a real solution to this problem really
          requires looking at our institutions of cultivating learners.
        </p><p>
          <strong>So how can one use LLMs for learning new languages?</strong>
        </p><p>
          I'll answer this straight with the technique I have been using, which
          I think can also work for beginners learning their first language.
          Though the learning process for total beginners will be a bit longer
          since they must also learn the foundational ideas behind today's
          languages. Thankfully, given that pretty much all mainstream languages
          are quite similar, this foundational learning only needs to be done
          from scratch once.
        </p><p>
          I start by generating lots of agent code for various non-critical
          things, and then spending a lot of time actually reading the generated
          code. This allows me to see the mechanics of the language in a real
          setting at a mere glimpse. Then, I continue to drive the agent by
          describing high-level designs in prompts, continuing to read/analyze
          the code along the way. After doing this enough times, I decide
          whether or not it's worth learning the language by hand based on how
          much I enjoy its mechanics from a glimpse. In other words, this
          process builds a high-level map of how to navigate the language, and
          forms a general opinion on whether or not I will like writing code in
          it.
        </p><p>
          The handwriting aspect is important because it forces you to
          understand the foundational mechanics that make up the language in
          great detail. This in-turn makes you better at using the agents in the
          future. However, the aspects I would focus on handwriting should not
          be glue code like basic IO, basic data transforms, and simple UI
          components, but rather things that the agent will struggle with.
          Namely: performance intensive code, library/advanced typing
          shenanigans, non-trivial memory management, security, etc. Good
          library design tends to cover all of these points, and will often
          require you to push the language to its conceptual limits via
          handwriting.
        </p><p>
          Another aspect of handwriting you can do while using an agent is
          "being a janitor". That is, take part in manually cleaning up agent
          code, since it tends to be verbose and/or slow in the general case.
          This will get you familiar with the various syntatic sugars and
          performance facilities found in the language, and will help you
          further craft a personal style.
        </p><p>
          Generally speaking, I think this process also makes it easier to learn
          languages in parallel. One of my current projects uses Rust, Go, and a
          bit of Python all at once, which I hadn't touched in a while (~2-3
          years for each) before starting the project (TypeScript is also used,
          but I'm already experienced with it). I was also able to use this
          technique to become proficient enough with C++ to do some open source
          contributions where much of the code was written by hand (this also
          required reading dedicated books).
        </p><p>
          That being said, none of this works if you don't like playing with
          ideas. So really, the difficulty is cultivating that.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>One Annoying Thing About Codex — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#one-annoying-thing-about-codex</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#one-annoying-thing-about-codex</guid>
      <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If left unbound, it over complicates things (I don't like the term
          over-engineering in a negative connotation, rather I prefer
          over-complicated). I don't need 500 guard clauses for each function,
          neither do I need 4,000 tests for a trivial helper method. Also, we
          don't need to add 20,000 additional fields to a simple outbox table
          "just in case". This is like me when I program with anxiety.
        </p><p>
          In planning, pay attention to anything it does, and I often will have
          it sketch interfaces/types to understand what it's about to generate.
          I often find that it will create types with many unecessary fields,
          methods, conformances, etc. So I tell it to cutdown, and it generally
          listens.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>This Site is a Stable Archive — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#this-site-is-a-stable-archive</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#this-site-is-a-stable-archive</guid>
      <pubDate>Thu, 07 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          A very common question I get asked is why I avoid platforms like
          LinkedIn in exchange for putting my thoughts/work this site. Now I'm
          not inherently against using social media in the future despite what
          I've said previous
          <a href="https://whypeople.xyz/global-notes#why-not-social-media-">notes</a>, but I would never treat those as "the profile". LinkedIn will
          eventually fail on a long enough timescale (could be decades), and
          with it any user data it stores will vanish into dust never to be seen
          again. Imagine all of your work that you decided to archive vanishing
          like that. Such is the way of the
          <a href="https://worrydream.com/TheWebOfAlexandria/">Web of Alexandria</a>.
        </p><p>
          I'm reviving this because of the major
          <a href="https://mrshu.github.io/github-statuses/#about">issues</a>
          that Github has been having recently. The amount of downtime and
          issues can potentially become a serious problem down the line, and
          it's already been problematic for lots of large projects. If the
          ending of Theo's recent
          <a href="https://youtu.be/HuE7OvOckfE?si=9ZGgwzyfeZO1JMT1">video</a>
          where he describes fragmentation plays out, and his theory of there no
          longer being a "singular profile" for any given person is true, then
          the only way to have such a profile is to create your own.
        </p><p>
          As we can see, if Github continues on its current trend, and more
          people begin migrating away, then anything stored on Github is
          inherently "lost" to more and more people. Eventually, Github will
          fail completely on a long enough timescale (could be decades), and
          with it all of it's repos will vanish. The only way out is to clone
          the repos elsewhere.
        </p><p>
          The information and code on this site is controlled entirely by me.
          It's not an inherently failable business, and I don't ever intend to
          make it one. I do use Netlify for hosting, and Netlify will eventually
          fail on a long enough timescale like any business, but I will just
          find a different hosting provider in that case. You likely won't even
          notice the disruption if that occurs unless you lurk here endlessly
          like some seem to (you know who you are).
        </p><p>
          So to reiterate, the reason I put effort into this site compared to
          social media platforms is because I at least know that I can keep this
          up as long as I'm still around.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>An Alternative to Feature Creep — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#an-alternative-to-feature-creep</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#an-alternative-to-feature-creep</guid>
      <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          In some of my earlier days, I would fall victim to the so-called
          feature creep because I wanted to build everything. This would cause
          me to at times not finish anything for long periods of time, despite
          spending lots of time writing code, desiging, etc. In the era of
          agents, I'm noticing the tendencies of more people to want to feature
          creep their stuff because they can just "prompt it into existence".
          Undoubtedly, this will cause massive feature balls to be created,
          where-in not all of the features will interact very well with each
          other.
        </p><p>
          Of course, the natural advice for getting around this is to "scope
          down", or essentially slash your creation until it becomes a dwarfed
          version of it's actual self. Then you may actually finish it!
        </p><p>There are 2 points I generally don't like about this:</p><ul>
          <li>
            Not everything <em>should</em> be finished.
            <ul>
              <li>
                The idea that we have to finish everything we start is really an
                arbitrary thing learned through schooling. Yet it's of the only
                things that leads to the "predicatability" we so crave, so it
                becomes the norm in the real world.
              </li>
              <li>
                Honestly, I would rather that a conceptually bad project never
                gets finished rather than unleashing a zombie in the real world.
              </li>
            </ul>
          </li>
          <li>
            If "cutting scope" is the correct answer, that inherently means
            we're creating something that isn't at it's fullest potential.
            <ul>
              <li>
                From an building-as-an-art standpoint, this hurts me
                emotionally.
              </li>
              <li>
                From a building central infrastructure standpoint, this hurts me
                logistically.
              </li>
            </ul>
          </li>
        </ul><p>
          However, things do need to get finished at some point, we can't just
          keep adding features endlessly. Therefore, how does a line actually
          get drawn here?
        </p><p>
          For me at least, this has been the result of a perception change. That
          is, instead of creating the thing to solve <em>the</em> problem
          entirely, instead create the thing that allows the end-user to solve
          <em>their</em> own problems. Or, in Theo's words, "Allow your users to
          do their bespoke bullshit". I view this as enhancing one's inherent
          ability rather than substituting it with a product they can't see the
          internals of or modify directly for their personal needs.
        </p><p>
          With that perception change, now the entirety of how we design things
          changes. The basic notion is that whatever is built has to be directly
          expandable by end-users. I've found that getting to this point has
          much smaller inherent "scope" whilst still being able to produce
          something useful. This is because now the focus shifts from "How many
          features does this need to please people?" to "How simple can this be
          to be adaptable in any reasonable situation?". In fact, I treat
          feature requests as bugs with this perception (If you have to ask me
          for the feature, what is missing that prevents you from making it
          yourself?).
        </p><p>
          Reaching such simplicity is hard, but it's a necessary part of systems
          design, especially for things that need to be stable. It also requires
          saying "no" in a professional context to features that don't benefit
          this simplicity, though this may not always be ideal personal
          reputation-wise.
        </p><p>
          Regardless, this is one of the things that I think library design
          makes you realize, since libraries by design have to be
          adaptable/extendable by principle. We often speak of "less is more"
          for this kind of reason.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Another Note About Indexing — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#another-note-about-indexing</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#another-note-about-indexing</guid>
      <pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Right now, the meta seems to be to either implement one kind of
          indexing or the other from the previous note. Nearly all apps these
          days index almost entirely mechanically, but some (eg. Collaborative
          stuff Excalidraw, Google Docs, etc.) are more intuition-based.
        </p><p>
          Only indexing mechanically takes away understanding or a chance to go
          on a journey to find that understanding (of which the journey itself
          produces learnings). Only indexing based on intuition can potentially
          lead one down a false path, and/or cause them to miss subtle and hard
          to find details. Both extremes lead to a lack of enlightenment in some
          way, mechanical-only leads to pure acceptance of black-boxed
          mechanical-standards, and intuition-only leads to conspiracy theories.
        </p><p>
          Funnily enough, not being able to understand a black-boxed mechanical
          mechanism also leads to conspiracy theories because the mechanical
          understanding becomes purely intuition-based. So really, black boxes
          can be quite bad for important things, especially when most people are
          not the kind who want or have the time/ability to deep dive and
          understand them.
        </p><p>
          For example, a lot of LLM-based tools will offer the ability to
          summarize readings, but the problem with these tools is often that
          they encourage the "summary" as a replacement for reading instead of
          an enhancement. An LLM-based summary is mechanical indexing while
          actually reading is intuition-based indexing, so only accepting the
          summary is like treating the actual reading as a black box. Now of
          course, not everything needs to be drawn out to 500 pages, but it's
          also no secret that more capable people are the ones who tend to read
          more.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>2 Kinds of Indexing — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#2-kinds-of-indexing</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#2-kinds-of-indexing</guid>
      <pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          The first is mechanical, in which information is indexed by analysis
          of its literal content. This is implemented using your typical
          textbook CS + ML algorithms. This is useful for pattern detection and
          "seeing relationships which cannot typically be seen".
        </p><p>
          The second is intuition, in which information is index by an explicit
          process that determines a set of information is related for some
          reason. This is typically implemented manually by humans as they
          determine which pieces of information are related, and a good UI helps
          with this. This is useful for creating and understanding.
        </p><p>
          Both of these seem to be essential properties for any dynamic
          information system.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Actors vs Locks (Performance) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#actors-vs-locks-performance</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#actors-vs-locks-performance</guid>
      <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Some may have watched a recent PointFree episode that detailed a
          performance comparison of actors and locks on a given cache. The gist
          of the episode shows that when it comes to individual calls, locks
          perform faster, but when you can group actor calls into 1 isolation
          hop then the actor performs faster.
        </p><p>ie. (Based on the episode code examples)</p><pre><code class="lang-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title">MutexCache</span> {
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> entries = Mutex&lt;[<span class="hljs-type">Int</span>: <span class="hljs-type">Int</span>]&gt;()

  <span class="hljs-keyword">func</span> <span class="hljs-title">set</span>(<span class="hljs-params">_ key: <span class="hljs-type">Int</span>, value: <span class="hljs-type">Int</span></span>) {
    <span class="hljs-keyword">self</span>.entries.withLock { $<span class="hljs-number">0</span>[key] = value }
  }
}

<span class="hljs-keyword">final</span> <span class="hljs-keyword">actor</span> <span class="hljs-title">ActorCache</span> {
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> entries = [<span class="hljs-type">Int</span>: <span class="hljs-type">Int</span>]()

  <span class="hljs-keyword">func</span> <span class="hljs-title">set</span>(<span class="hljs-params">_ key: <span class="hljs-type">Int</span>, value: <span class="hljs-type">Int</span></span>) {
    <span class="hljs-keyword">self</span>.entries[key] = value
  }
}

<span class="hljs-keyword">let</span> actorCache = <span class="hljs-type">ActorCache</span>()
<span class="hljs-keyword">let</span> mutexCache = <span class="hljs-type">MutexCache</span>()

<span class="hljs-comment">// 1. Mutex Cache is Faster</span>
<span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>...<span class="hljs-number">10_000</span> {
  mutexCache.set(key, value: key + <span class="hljs-number">1</span>)
}
<span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>...<span class="hljs-number">10_000</span> {
  <span class="hljs-keyword">await</span> actorCache.set(key, value: key + <span class="hljs-number">1</span>)
}

<span class="hljs-comment">// 2. Actor Cache is Faster</span>
<span class="hljs-keyword">extension</span> <span class="hljs-type">Actor</span> {
  <span class="hljs-keyword">func</span> <span class="hljs-title">run</span>&lt;<span class="hljs-type">T</span>&gt;(_ fn: <span class="hljs-meta">@Sendable</span> (<span class="hljs-keyword">isolated</span> <span class="hljs-keyword">Self</span>) -&gt; <span class="hljs-type">T</span>) -&gt; <span class="hljs-type">T</span> {
    fn(<span class="hljs-keyword">self</span>)
  }
}

<span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>...<span class="hljs-number">10_000</span> {
  mutexCache.set(key, value: key + <span class="hljs-number">1</span>)
}
<span class="hljs-keyword">await</span> actorCache.run { actorCache <span class="hljs-keyword">in</span>
  <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>...<span class="hljs-number">10_000</span> {
    actorCache.set(key, value: key + <span class="hljs-number">1</span>)
  }
}
</code></pre><p>
          Intuitively, this makes sense since each call to <code>set</code> on
          <code>MutexCache</code> will have to acquire a lock, whereas all the
          calls to <code>set</code> on <code>ActorCache</code> can be grouped
          into a single isolated access (which effectively removes the
          overhead).
        </p><p>
          However, there's more to this, as right now the
          <code>MutexCache</code>
          embeds a lock internally, but what if we did this instead.
        </p><pre><code class="lang-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title">NonSendableCache</span> {
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> entries = [<span class="hljs-type">Int</span>: <span class="hljs-type">Int</span>]()

  <span class="hljs-keyword">func</span> <span class="hljs-title">set</span>(<span class="hljs-params">_ key: <span class="hljs-type">Int</span>, value: <span class="hljs-type">Int</span></span>) {
    <span class="hljs-keyword">self</span>.entries[key] = value
  }
}

<span class="hljs-keyword">let</span> nonSendableCache = <span class="hljs-type">Mutex</span>(<span class="hljs-type">NonSendableCache</span>())
</code></pre><p>
          Now, you can see that we made a simple cache that was non-Sendable,
          which means that we don't even have to worry about concurrency in its
          implementation. Then, we create a local variable that wraps the
          non-Sendable cache in a <code>Mutex</code>. This allows to do the
          following:
        </p><pre><code class="lang-swift">nonSendableCache.withLock { nonSendableCache <span class="hljs-keyword">in</span>
  <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>...<span class="hljs-number">10_000</span> {
    nonSendableCache.set(key, value: key + <span class="hljs-number">1</span>)
  }
}
</code></pre><p>
          This is essentially the same thing as calling the
          <code>run</code> helper on <code>ActorCache</code>, but for mutexes
          instead. Now because we only have a single grouped access from both
          <code>ActorCache</code> and our <code>NonSendableCache</code>, the
          performance is essentially the same for our benchmark operation. Since
          the number of isolation hops/mutex acquisitions is 1, the overall
          synchronization overhead is practically negligible.
        </p><p>
          However, the benefit of <code>NonSendableCache</code> is that we also
          get to use it inside an actor if we want to defend it, and everything
          is still all well and good.
        </p><pre><code class="lang-swift"><span class="hljs-keyword">final</span> <span class="hljs-keyword">actor</span> <span class="hljs-title">ActorCache2</span> {
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> cache = <span class="hljs-type">NonSendableCache</span>()

  <span class="hljs-keyword">func</span> <span class="hljs-title">set</span>(<span class="hljs-params">_ key: <span class="hljs-type">Int</span>, value: <span class="hljs-type">Int</span></span>) {
    <span class="hljs-keyword">self</span>.cache.set(key, value: value)
  }
}
</code></pre><p>
          So really, the lesson here is that non-Sendable code will avoid any
          synchronization overhead by default, and allows us to defer
          Sendability concerns to the minimal points where it's absolutely
          necessary. I wrote about this
          <a href="https://whypeople.xyz/non-sendable-core-sendable-shell">here</a>.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Terms of Time — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#terms-of-time</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#terms-of-time</guid>
      <pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Most would say that 2-3 years is long term, I disagree. 2-3 years is
          very little, and I consider it to be short term. Generally, this is
          the frequency in which we get a new tech trend, with agents being our
          current one. Furthermore, cultures only shift slightly in this
          timeframe (mostly toward the incremental trend). Larger culture shift
          itself takes a lot longer, and is necessary for invention to prosper.
        </p><p>
          For me, long term has to mean decades, because that’s generally how
          long it takes for a culture to completely adopt new ways of thinking
          (and for the old generation to start yelling at clouds apparently)
          required to successfully adopt new inventions. Don Norman notes that
          such a process is roughly 20-30 years (eg. See the HD TV example from
          <em>The Design of Everyday Things</em>), and this is also the
          timeframe that Alan Kay and the rest of Xerox PARC operated on with
          respect to their research (eg. Dynabook was sketched in 1968, laptops
          came in the 90s, and the iPad released in 2010).
        </p><p>
          Another aspect about focusing on the 20-30 year timeframe is that it
          becomes essentially impossible to predict the state of that future
          from today. If I were to ask you what programming would look like in
          5-10 years, you would probably answer around agents and AI taking over
          things. However, you wouldn’t answer with that if I changed the
          timeframe to 30 years because our culture would be entirely different
          then. Instead, the only way to predict that future is to literally
          invent it if we want to adopt the Alan Kay way of saying things. That
          is, long term predictions are much more about realizing visions rather
          than trying to predict where the current trajectories will take us.
        </p><p>
          This leads us to the nature of those visions. You may think that it
          would be ideal to draw some sort of futuristic technology, and call it
          the vision. This is fine for the short (0-5 years) or medium term
          (5-10 years) scale, but reminder that the difficulty is dealing with
          the culture. Therefore, I would stand to think that the vision needs
          to be based around an ideal culture instead of an ideal technology.
          For Doug Engelbart, this was the notion of augmenting intellect,
          boosting collective IQ, and improving our ability to improve (if you
          read his
          <a href="https://www.dougengelbart.org/pubs/augment-3906.html">1962 paper</a>, computers are not the central focus).
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Clean Code is Good UI Design (4/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#clean-code-is-good-ui-design-4-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#clean-code-is-good-ui-design-4-n-</guid>
      <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you know ARM NEON, you would know immediately what the following
          code does.
        </p><pre><code class="lang-c">
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">f</span>(<span class="hljs-params">__fp16* <span class="hljs-keyword">out</span>, <span class="hljs-keyword">const</span> __fp16* values, size_t values_size</span>) </span>{
    <span class="hljs-keyword">for</span> (size_t i = <span class="hljs-number">0</span>; i &lt; values_size; i += <span class="hljs-number">8</span>) {
        vst1q_f16(<span class="hljs-keyword">out</span> + i, vaddq_f16(vld1q_f16(values + i), vld1q_f16(<span class="hljs-keyword">out</span> + i)));
    }
}
        </code></pre><p>
          However, I assume most of you reading this don’t, so here’s what it
          actually does.
        </p><pre><code class="lang-c">
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sum_all</span><span class="hljs-params">(<span class="hljs-keyword">float</span>* out, <span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span>* values, <span class="hljs-keyword">size_t</span> values_size)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; values_size; i++) {
        out[i] += values[i];
    }
}
        </code></pre><p>
          This really shows that the lack of understanding came from the fact
          that the “UI” for the NEON version looks quite cryptic for such a
          simple operation. That is, one doesn’t know what
          <code>vst1q_f16</code> means unless they’ve read the ARM NEON ISA.
        </p><p>
          Furthermore, if you want to make the NEON version handle sums properly
          when <code>values_size</code> is not a multiple of 8, you would need
          to add the scalar section at the end to handle the remaining values. I
          didn’t include that bit in the first NEON example because it would’ve
          given the answer away.
        </p><pre><code class="lang-c">
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sum_all_neon</span>(<span class="hljs-params">__fp16* <span class="hljs-keyword">out</span>, <span class="hljs-keyword">const</span> __fp16* values, size_t values_size</span>) </span>{
    size_t i = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (; i + <span class="hljs-number">8</span> &lt;= values_size; i += <span class="hljs-number">8</span>) {
        vst1q_f16(<span class="hljs-keyword">out</span> + i, vaddq_f16(vld1q_f16(values + i), vld1q_f16(<span class="hljs-keyword">out</span> + i)));
    }

    <span class="hljs-keyword">for</span> (; i &lt; values_size; i++) {
        <span class="hljs-keyword">out</span>[i] += values[i];
    }
}
        </code></pre><p>
          Another thing worth noting is that you would generally not write NEON
          code like that as an isolated function, but rather as a part of a
          fused mathematical operation in practice.
        </p><pre><code class="lang-c">
<span class="hljs-keyword">void</span> fused<span class="hljs-number">_</span>op(<span class="hljs-number">__f</span>p<span class="hljs-number">16</span>* <span class="hljs-keyword">out</span>, const <span class="hljs-number">__f</span>p<span class="hljs-number">16</span>* values, size<span class="hljs-number">_</span>t values<span class="hljs-number">_</span>size) {
    size<span class="hljs-number">_</span>t i = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (; i + <span class="hljs-number">8</span> &lt;= values<span class="hljs-number">_</span>size; i += <span class="hljs-number">8</span>) {
        float<span class="hljs-number">16</span>x<span class="hljs-number">8</span> vals = vld<span class="hljs-number">1</span>q<span class="hljs-number">_f</span><span class="hljs-number">16</span>(values + i);
        float<span class="hljs-number">16</span>x<span class="hljs-number">8</span> <span class="hljs-keyword">out</span><span class="hljs-number">_</span>vals = vld<span class="hljs-number">1</span>q<span class="hljs-number">_f</span><span class="hljs-number">16</span>(<span class="hljs-keyword">out</span> + i);
        <span class="hljs-comment">// Other math ops that use vals and out_vals...</span>

        vst<span class="hljs-number">1</span>q<span class="hljs-number">_f</span><span class="hljs-number">16</span>(<span class="hljs-keyword">out</span> + i, vaddq<span class="hljs-number">_f</span><span class="hljs-number">16</span>(vals, <span class="hljs-keyword">out</span><span class="hljs-number">_</span>vals));

        <span class="hljs-comment">// Other math ops that use vals and out_vals...</span>
    }

    <span class="hljs-keyword">for</span> (; i &lt; values<span class="hljs-number">_</span>size; i++) {
        <span class="hljs-comment">// Handle remaining values via scalar loop...</span>
    }
}
        </code></pre><p>
          There are a whole host of reasons for why that is, but the issue is
          that such reasons are completely invisible to you whether or not you
          write code by hand or through an agent. Agents, from my experience,
          unfortunately tend to fall for the human tendencies of microbenchmark
          optimization (at the expense of E2E performance) when it comes to
          these kinds of things, so it isn’t of much help on its own. So even
          the agent can have blindspots despite having far more trained
          knowledge than you ever will.
        </p><p>
          In nearly all tools that deal with code today (Text Editors, IDEs,
          Agent CLIs, Github, etc.), context about the production environment is
          essentially invisible from within the tool itself. That is, all code
          is displayed with the same visual hierarchy, regardless of how
          critical or uncritical it is. This means that the context has to live
          somewhere else, which is often one’s head or in model context/weights.
          I wonder how well that’s worked so far.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Plugins — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#plugins</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#plugins</guid>
      <pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          My recent notes have mentioned a “universal medium” for apps to
          communicate with each other. However, some might ask, why not design a
          plugin system for your app? I’ll have to refer to the term “plugin” in
          the sense that it seems to be defined at large, which is “an
          extendable module to a particular application”. Technically, the
          “universal medium” could also be described as a “plugin system”, but
          not in the sense that we typically think of plugins so it’s best to
          stick with the at large definition for this.
        </p><p>
          For the record, I don’t think the idea of plugins are a bad thing, but
          they are not a solution to the general application silo problem.
          Plugins can be fine under the following conditions:
        </p><ul>
          <li>
            The API/architecture supporting the plugins is designed for maximal
            customization.
          </li>
          <li>
            The plugin is specifically designed for the application that exposes
            a plugin system.
          </li>
        </ul><p>
          The main issue is knowledge, particularly that the plugin author has
          to know the application that it’s interacting with. The real idea in
          breaking the application silo is to make it possible for multiple apps
          to communicate
          <em>without knowing about each other or their intimate details</em>.
          This is fundamentally impossible with plugins in the way they are
          adopted at large.
        </p><p>
          This knowledge problem is also an issue with how we interact with
          web-services as well. For instance, you may have your app call
          OpenRouter, but what if another service comes along that’s better
          somehow? In today’s world, you would have to manually (or get your
          agent to), deploy an update to switch to the new service. This is
          because somewhere in your app there’s a hardcoded URL string that
          points to OpenRouter directly, so if you want to get around that you
          need a service discovery mechanism. Thankfully, correlation and
          pattern matching are what AI tends to be good at.
        </p><p>
          Now, at some point you will have to know at least one of the thing’s
          you’re interacting with. That is, you will have to have a hardcoded
          interaction with <em>something</em> (URL, file, database, etc.) to get
          the process of discovery started. However, that
          <em>something</em> ideally is a mechanism for finding “all the things”
          you need, and therefore you limit the number of hardcoded interactions
          in exchange for dynamic ones.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>How do we break the Application Silo? — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#how-do-we-break-the-application-silo-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#how-do-we-break-the-application-silo-</guid>
      <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Recently, I wrote about the need for the “next killer effort
          contributed to by everyone”, but the only way that’s going to happen
          is if we stop thinking about software as winner-take-all monoliths.
          For every 1 winner-take-all monolith made by a handful of people, the
          other thousands of players are immediately sent into retirement never
          to compete again. Thus, the monolith is forced to grow bigger in both
          features and complexity as everyone is forced to adopt it.
        </p><p>
          On one hand, the monolith becomes very capable due to the fact that it
          must grow its featureset to match the expectations of users. On the
          other hand, it’s complexity grows boundlessly, eventually requiring
          armies of engineers, contributors, and now agents just to maintain it
          all. This, alongside the fact that many users have become dependent on
          the monolith, means that progress slows and stagnation begins.
          Perhaps, once the source code leaks this truth is revealed, but
          instead we’ll often pretend like nothing happened because Garry Tan
          just announced that he hit a rate of 100k LOC per day. Just kidding,
          but G-Kernel when? (It would only take 810 days at his actual current
          rate.)
        </p><p>
          This is why it took LLMs to break the stronghold that traditional IDEs
          had on developers for decades. Throughout that period IDEs were
          stagnant with only minor incremental improvements year-over-year, and
          the cost of creating and distributing a new one was extremely high
          such that no one but the already established players could do it.
          Furthermore, the established players didn’t want to take the risk of
          creating something qualitatively different, so they never did and thus
          stagnation.
        </p><p>
          The way to defeat this is not to build more monoliths, but rather to
          build smaller and more well round components that can all interact
          with each other. For instance, I’ve been enjoying cmux as of late, but
          I don’t care about its built-in browser and rarely use it. The reason
          for this is because I already use an actual browser (Zen), and I would
          rather use Zen than a bolted on <code>WKWebView</code>. However,
          there’s no way for cmux and Zen to talk to each other, which is why
          cmux added a built-in browser.
        </p><p>
          So the question then becomes one of what the communication protocol
          looks like rather than one of “breaking the application silo” (that’s
          already been answered). There have been many of these that have been
          proposed or implemented, but all don’t solve the problem directly for
          all sorts of reasons. This is primarily because those solutions
          (Smalltalk, Etoys, etc.) tend to create their own isolated world with
          their own isolated constructs. This is great for purity, especially if
          the intention is to redesign something qualitatively different, but
          unfortunately doesn’t change the current state of things.
        </p><p>
          To change the current state of things, you’ll need 2 things. One of
          which is a technical problem, but the second and bigger one is a human
          problem.
        </p><ul>
          <li>
            A variety of transports (HTTP, Websockets, In-Memory, etc.) for the
            “messages”, and constructs for the “objects” (any existing runnable
            OS process or code).
          </li>
          <li>People to universally adopt the protocol.</li>
        </ul><p>
          The second issue is why systems like Smalltalk haven’t been adopted at
          large, and also exactly why we still have this problem. Monoliths have
          been normalized to the point where very few will ever perceive of an
          alternative. That is, the application silo isn’t seen as a problem in
          the first place, and won’t ever be if we keep building the way we
          currently are.
        </p><p>
          I have to say that starting a system like this to interop with
          existing applications is inherently starting from a non-idealistic
          point of view. Truthfully, if one wound the clock back enough to the
          80s, it would’ve been more ideal to design today’s systems with this
          view upfront, but I would still say that some progress can be made by
          starting from our current point. That is, it can hopefully become a
          comfortable step from a tricycle to a bicycle.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Observations on 3 Months of Agents — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#observations-on-3-months-of-agents</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#observations-on-3-months-of-agents</guid>
      <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          It’s been about 3 months since I started seriously using agents in my
          workflow, so it’s time to share some thoughts.
        </p><ul>
          <li>
            OpenCode using GPT 5.4 as my primary model. Occasionally MiniMax to
            see the progress of open weight things, and for smaller tasks.
          </li>
          <li>
            3-4 agents in parallel seems to be the sweet spot for productivity
            for a focused session.
          </li>
          <li>
            I’ve been able to build internal tools for various needs faster than
            ever before.
          </li>
          <li>I read the code.</li>
          <li>
            I still like handwriting code, just not boilerplate or glue as much.
            <ul>
              <li>
                The parts that feel more artistic are the parts that I still opt
                to handwrite.
              </li>
              <li>
                For lower-level things, agent code often isn’t good enough on
                it’s own, though it may get something started.
              </li>
              <li>
                Neither do I think my handwriting skills have atrophied. Writing
                more glue and boilerplate code does not keep your skills sharp,
                but rather taking on actually challenging things does.
              </li>
              <li>
                I still think handwriting will be around for the next few years,
                but eventually I do believe it will be completely suceeded by
                future tools in the distant future.
              </li>
            </ul>
          </li>
          <li>
            Agents tend to optimize for microbenchmarks.
            <ul>
              <li>
                If you tell the agent that a function is performance sensitive,
                it will try everything in it’s power to optimize the one
                function without considering the surrounding environment
                (especially hardware characteristics).
              </li>
              <li>
                Eg. Introducing a 4kb lookup table for a calculation that runs
                once in a logit sampling kernel.
              </li>
            </ul>
          </li>
          <li>
            Agents make ~3-4 subtle mistakes per 1000 lines of code.
            <ul>
              <li>
                You will need a review system in place for this reason. Codex
                code reviews seem to catch some of these mistakes, but I haven’t
                yet tried a dedicated code review tool like Greptile or Code
                Rabbit (Copilot review doesn’t seem to give useful suggestions
                on PRs that I’ve seen from various open source projects).
              </li>
            </ul>
          </li>
          <li>
            Mostly writing Swift and TypeScript (professionally + free time),
            however a bit of C++ (free time) and Go (internal tool) has also
            been written as of late.
            <ul>
              <li>
                Swift tends to be the biggest struggle (especially for MiniMax).
              </li>
            </ul>
          </li>
          <li>
            I don’t feel like I’ve lost too much identity or meaning, primarily
            because architectural design was always a fun part (and now it gets
            more focus).
            <ul>
              <li>
                As stated before, I still like handwriting artisinal code, but
                boilerplate and glue are not artisnal code.
              </li>
              <li>
                I also work with a small creative team, and like my environment.
                If I were in a soul-sucking enterprise where being able to write
                code was the last level of any work satisfaction I had, then
                maybe I would be quite depressed.
              </li>
            </ul>
          </li>
          <li>PRs have gotten larger.</li>
          <li>
            Team discussions have shifted away from specific codebase details to
            more design/business related topics.
          </li>
          <li>
            Constant refactors are necessary, because agents love large and
            verbose functions.
            <ul>
              <li>
                This has always been a thing, but now you must remain on top of
                it earlier in a project life-cycle.
              </li>
              <li>
                Agents tend to write more verbose code (eg. for-loops instead of
                using more concise collection transforms or other monadic
                behaviors), and you can often condense their output if you
                rewrote by hand.
              </li>
            </ul>
          </li>
          <li>
            I find myself branching out into more areas of software.
            <ul>
              <li>
                Mobile is one of the categories that more and more vibe-coders
                are entering. I have nothing inherently against vibe-coders
                provided their motivations are for the greater good, and I’m
                happy with the fact that more people have the chance to create
                something.
              </li>
              <li>
                However, if a vibe-coder can do much of what I was previously
                doing, then I would rather do something that requires more
                expertise. This has caused me to seek out contributing to
                lower-level projects like inference engines.
              </li>
              <li>
                That being said, I will still be working on apps in the future,
                but the nature of them will inherently involve more technical
                complexity as a result. In one sense, this can help with
                building standout features. On the other, it can make me lose
                sight of what I’m actually creating, so care will need to be
                taken.
              </li>
              <li>
                From a larger-scale business angle, I’m also not sure that just
                a mobile prescence will be enough going forward since the cost
                of building mobile apps is lower to the point where anyone can
                do it.
              </li>
              <li>
                This branching sort-of started early last year when I began
                working on libraries instead of apps in my free time. In fact, I
                would say that a good library is harder to get right than many
                apps.
              </li>
            </ul>
          </li>
          <li>
            Trust from the marketplace is definitely at an all time low.
            <ul>
              <li>
                Take Reddit for instance, where a few years ago posting an ad
                for a good app in relevant subreddits was generally seen as
                something valuable.
              </li>
              <li>
                Since the cost of building is now much lower, many people are
                spamming ads for their vibe-coded app on various subreddits,
                which has caused lots of backlash (especially given the lower
                regards to quality).
              </li>
              <li>
                This makes it more strenuous to engage with communities, because
                now there’s more upfront time cost needed to build proper trust.
              </li>
            </ul>
          </li>
          <li>
            Ralph Loops are an interesting technique for boring stuff.
            <ul>
              <li>
                They’ve also been a good way to get something started. Whereby
                after an initial version is built, you go back to a more engaged
                development process.
              </li>
            </ul>
          </li>
          <li>
            Less third-party tools are necessary because it’s not as time
            consuming to build stuff in-house now.
            <ul>
              <li>
                If a library or service doesn’t handle something of sufficient
                complexity or standard functionallity (eg. hosting
                infrastructure, databases, inference, standardized algorithms,
                frameworks, etc.), then I find that it’s now viable to build an
                in-house version of said service or library.
              </li>
              <li>
                This mostly applies to libraries that mostly simplified syntax
                (eg. react-hook-form)
              </li>
            </ul>
          </li>
          <li>
            Velocity increases 10x when I’m not thinking about design or the
            details of what is being generated.
            <ul>
              <li>
                When I do care, it drops massively. In general, caring about
                details will drop velocity because it’s more work than not
                caring. This is not exclusive to agentic coding.
              </li>
              <li>
                I’m also not interested in a LOC generated per day contest.
                Anyone can easily hit 10k per day by not caring deeply about the
                intimate details (see
                <a href="https://x.com/Gregorein/status/2038953944475472316">this</a>).
              </li>
              <li>
                Furthermore, the ability to increase production without a
                similar ability to increase visibility and understanding has
                historically created many second order problems. (eg. Injuries +
                health problems + work conditions caused by the Loom requiring
                labor laws to prevent further harm during the industrial
                revolution.)
              </li>
            </ul>
          </li>
          <li>
            Thinking and understanding is more important than ever.
            <ul>
              <li>
                This is a new black box, a new layer of abstraction, and thus a
                new generation of people who have no idea how things actually
                work will awaken.
              </li>
              <li>Everyone is now yet another step removed from something.</li>
              <li>
                Part of that something was bad, and thus it’s good that we’re
                removed from it.
              </li>
              <li>
                However, part of that something was necessary, and now that
                we’re removed from it the meaning is lost.
              </li>
            </ul>
          </li>
        </ul><p>
          In essence, we have a new tool that increases productivity. Yet, like
          other such production tools that came before it, it does very little
          to boost understanding of what is being produced. Therefore, the
          understanding part is largely an exercise left to the wielder that can
          easily be, and is often deliberately, ignored.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Theory and Practice — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#theory-and-practice</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#theory-and-practice</guid>
      <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Inherently one would consider this note theoretical because it
          discusses ideas instead of being directly applicable to the “real
          world”. This is ironic, because the act of writing, or otherwise
          producing abstract symbology to <em>describe</em> rather than
          <em>do</em> is what creates this distinction.
        </p><p>
          What further makes this annoying is the idea that “In theory, theory
          and practice are the same, but in practice they are not.” Therefore,
          if I mention that the 2 ideas are the same thing in this note, we’re
          still stuck in theory. In fact, I already referred to them as ideas,
          which inherently puts them on the same base playing field. If I
          instead referred to them as “truths”, then once again we put them on
          the same base playing field.
        </p><p>
          Another annoyance is the idea that the “real world is messy”, and
          therefore practical solutions must also be “messy” because of this.
          This prevents me from creating a simple vacuum to show how theory and
          practice relate to each other. Of course, this writing is already such
          a simple vacuum, but so is a conversation, social media post/comment,
          any form of communication, etc.
        </p><p>
          I suppose you can see my annoyances with this line of thinking, and
          what makes things worse is that we tend to dismiss the theoretical
          entirely because of it. However, it’s also my observation that the
          best practitioners that I’ve read in the art of Software had a great
          grasp on both “theoretical” and “practical” ideas, whereas the worst
          only tend to focus on technologies and frameworks deemed as
          “practical”.
        </p><p>
          In terms of economic value, many would say this distinction between
          theory and practice is massive, and will always use it to lower the
          value of education (theoretical). However, it’s often those who manage
          to apply complex theoretical ideas in practice that make the most
          money, and this is especially true today in the AI era. In fact, the
          theoretical complexity is often used as leverage! 99% of the
          population has never heard of a “transformer”, or probably don’t even
          understand what “deep learning” means at even a basic level. Yet, a
          growing number of those people are using and becoming dependent on
          Claude and other models every day.
        </p><p>
          To those people, learning “AI” means learning how to use Claude to get
          work done, not learning the underlying principles of deep learning.
          I’ll be countered here with this, “If you don’t work in machine
          learning, then why would you spend time learning deep learning
          principles? How are those principles practical for my purposes?” One
          response could be that it’s great to always have deep knowledge of
          things regardless, and therefore if you have the chance and
          inclination, then you should put the learning hours in.
        </p><p>
          On the other hand, most people don’t think that way, so I’ll observe
          that the best practitioners tend to know a great deal about at least
          what happens 1 abstraction layer below their practice. That is, people
          who know how to code will likely use agents better than those who
          don’t. Furthermore, those who understand deep learning can tell when
          it is being used for both good and evil, which may or may not align
          with the general public’s perception of the matter.
        </p><p>
          For those reasons above are why I don’t like the distinction between
          the theoretical and practical. Then the main question becomes, how do
          we bridge the distinction?
        </p><p>
          To that, my answer is that we look at communication. No, I’m not going
          to say that everyone needs to work on their communication skills,
          though it can’t hurt. Rather, I’m looking at what mediums are
          available to us to communicate ideas. Generally speaking, if a typical
          person (ie. not one that likes abstract ideas) cannot see, feel, or
          experience an idea in a form understandable to them, it might as well
          be both invisible and useless to them.
        </p><p>
          This is in spite of the fact that such an idea may indeed be useful to
          them in the long term, however it’s understandably quite difficult to
          perceive the long term (even for smart people). By long term, I don’t
          mean a handful of months or a few years, but rather decades. This is
          probably why the only successful predictors of the long-term future
          have been from inventors themselves.
        </p><p>
          So the right questions to ask would be related to whether or not
          writing, social media, video, photos, etc. are the right way to
          communicate long-term ideas in the first place (this note is ironic in
          that sense). In my view, given how each of these tend to lack context
          of some kind (eg. Writing lacks visuals, visuals lack precision,
          social media lacks any context whatsoever), I would say there’s quite
          a bit of work to be done here. We need to make the invisible visible,
          otherwise the rest of the world doesn’t care.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notes on a Better Commercial Editor (2/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notes-on-a-better-commercial-editor-2-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notes-on-a-better-commercial-editor-2-n-</guid>
      <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          The first part of this was written before many people began writing
          most of their code with agents, and more-or-less around the time when
          many were realizing what agents could do yet still writing most of
          their code by hand. Nowadays, outside of the most low-level and
          performance critical stuff, I find this dynamic to have massively
          changed. In many such cases, I’ve found the only times where
          handwritten code is faster to write than agentic code is for simple
          line changes. This is especially the case for typical UI code which
          tends to be quite hacky and unstable in general.
        </p><p>
          That being said, there’s now more interest than ever in creating
          better editors and tools for this new paradigm. T3Code and cmux are
          very recent attempts that come to mind, and I’m sure more in a similar
          vein will come about in the future.
        </p><p>
          In past notes, I’ve also not been favorable towards TUIs despite using
          OpenCode and Ghostty daily. I should rephrase my sentiment I suppose.
          What I dislike about the terminal as a basis for everything is a lack
          of feedback when typing commands, or in other words “flying blind”. If
          a TUI can offer such feedback, or otherwise be useful in the same ways
          as a GUI, then for all intensive purposes it’s essentially the same as
          a good GUI. That being said, if a GUI offers no such feedback either,
          it’s essentially a terminal (like ChatGPT). So really, the bigger idea
          is exploration and learning through real-time feedback (game design
          emphasizes this point).
        </p><p>
          However, I still don’t think any existing solutions are approaching
          the mark for what is actually needed. From the
          <a href="https://whypeople.xyz/global-notes#notes-on-a-better-commercial-editor-1-n-">first note</a>
          in this series, the “collaborative programmable whiteboard-like space
          approach” with arrows to represent relationships is still something I
          think could be a step in this dirction if it was able to be a
          communication system between various different apps. Tools like cmux
          also show a hint in this direction as well by not imposing any
          specific app in their layout/notification system. That is, there’s no
          “built-in” agent harness, and you can choose to use a dedicated one
          like OpenCode, Claude Code, or Codex depending on what you prefer
          alongside any other TUIs/CLI tools you use. Rather cmux acts as the
          binding force between those tools.
        </p><p>
          In the short term (ie. 1-5 year timescale, real “long term” IMO is
          decades), I think this direction is better than monolithic apps that
          bake a bunch of features into them. That is, if we’re not talking
          about any escaping the 14-inch folding rectangle on my lap kinds of
          approaches (AKA a topic for another time), which is why I had to
          clarify that I meant “short term”.
        </p><p>
          That being said, I think we can do better than traditional
          side-by-side layout when it comes to having multiple apps on screen.
          The relationional arrows from my whiteboard approach in particular are
          a concept borrowed from project Xanadu. Notice how the arrows are not
          unidirectional, and instead represent a 2-way binding between parts of
          documents. I think this aspect in particular could be used between
          apps to mention related content.
          <img src="./global-notes/assets/better-commercial-editor-2-1.jpeg" alt="A mockup of the Xanadu UI in 1972.">
          <img src="./global-notes/assets/better-commercial-editor-2-2.png" alt="Xanadu Space, a 2007 prototype that does the Xanadu UI in 3D.">
          This later particular image is of a 2007 prototype called Xanadu
          Space, which does the Xanadu UI in 3D thus adding more depth.
        </p><p>
          Another aspect encoded in the first design was realtime feedback from
          each and every action. While of course, this was an idea largely
          developed in the 60s and 70s (especially so for the idea of live
          programming), some of Bret Victor’s work pre-Dynamicland (circa
          2010-2013) is quite infamous for this. Though Dynamicland has
          naturally expanded upon it into the physical world. Regardless, my
          <a href="https://vimeo.com/906418692">favorite talk</a> to this date
          has some examples of this in the digital realm. I should also mention
          that the reason for the talk being my favorite is not because of the
          demos that show “live programming”, but rather the second half which
          goes into a lecture on the meaning of work. Suffice to say, I would
          like to stray away from such spiritual topics in this note.
        </p><p>
          One of the biggest issues that I’m seeing with much of the ongoing
          work towards better tools is the idea that a single app that becomes
          the next editor is the ideal end-state. This is quite barbaric and
          competetive over collaborative in my opinion. For me at least, it’s a
          zero-sum game if only one or a handful of monolithic tools “win” and
          the rest become irrelevant.
        </p><p>
          This is what happened with the previous generation of text editors
          before the AI boom, and as a result no new radical UI innovations came
          for decades until the AI boom happened. For the established editors
          such radical innovation was too risky, and new players couldn’t easily
          enter (until AI) because the incumbents already had the market share.
          Also getting people to change their workflows is incredibly difficult,
          even if it’s to something better. The AI boom is the only reason that
          <em>some</em> (many outside of traditional tech hubs are almost
          entirely ignoring this) people are more open to change, but it will
          not last forever.
        </p><p>
          I would prefer that we are not locked with a handful of standard and
          stagnant editor UIs for the next 2 decades because no new competitor
          can easily break established workflows. Now is the our window to break
          much of the traditional application silo as we can. It’s true that
          most apps will remain monolithic, because breaking the silo requires a
          complete mindset shift that I’m not sure is easily possible at scale
          in a time frame of a few years. However, we can at least make things
          better than they have been in the past.
        </p><p>
          A great example of this kind of system from a UI perspective can be
          found in Alan Kay’s
          <a href="https://youtu.be/AnrlSqtpOkw?si=vPdCNiqOu8WR2G98&amp;t=595">tribute</a>
          to Ted Nelson. In today’s world, one would need to write or generate
          thousands of lines of code to make a dedicated app for what is seen in
          the demo. However, nearly zero new lines of code need to be added in a
          Smalltalk system to achieve the same functionallity.
        </p><p>
          Fun fact, the entirety of Smalltalk-76 was only ~10,000 lines of code.
          Not because the programmers were necessarily Carmack-tier, or because
          Smalltalk was such a great textual language. Rather, the system was
          designed such that few lines of code needed to be written in the first
          place. There was no separation between the GUI and programming itself,
          just dragging things around constituted “programming”. This was in
          1976 by the way.
        </p><p>
          The key idea that is shown in the Smalltalk system from Alan Kay’s
          tribute is that there is a way for each object to communicate with
          each other without direct knowledge of their existence. This way, one
          can focus on building “separate apps”, but still have them all
          communicate together in one cohesive and hyper-personalized
          experience. UNIX is somewhat an example of this in terminal-land, but
          an ideal solution is more for traditional UIs (and even TUIs). Fun
          fact, an ancient (2005) Bret Victor
          <a href="https://worrydream.com/MagicInk/#engineering_inference_from_the_environment">paper</a>
          actually describes what such a communication system could look like
          for GUIs. However, I’m sure we can make a more advanced one in today’s
          world that isn’t named MCP. MCP is only an agent protocol that has to
          be conciously invoked by an agent, where the agent has to know the
          tools available to it. We need something much more event-driven, where
          no 2 apps need to have explicit knowledge of each other in order to
          communicate.
        </p><p>
          One could argue that OpenClaw is fulfilling this “communication” need
          between different apps, but if that were the case we wouldn’t be
          having this discussion. OpenClaw is more focused on “doing things”
          rather than “understanding things”, which is why most of us are still
          writing code with tools like Codex, Claude Code, Cursor, OpenCode,
          etc. What I actually think we need is the inverse of OpenClaw, which
          implies an explicit focus on “understanding” (which in turn would aid
          us in the “doing” part).
        </p><p>
          This would imply less apps that bundle an agent panel, diff viewer,
          browser, terminal, code reviewer, etc. into 1 monolithic app. Rather,
          those would all be separate (and possibly developed by separate
          individuals and teams), but in turn would focus more efforts on
          communication with each other.
        </p><p>
          To give a simple model of thought, imagine if OpenCode could directly
          communicate with whatever browser you’re using, no matter if its Zen,
          Firefox, some chromium browser, Safari even (lol Apple would never),
          etc. and vice-versa? Today, either OpenCode would have to implement a
          browser feature (not happening), or all of the browsers would have to
          implement a coding agent (more likely, but still likely not a priority
          feature).
        </p><p>
          However, for the case of OpenCode implementing a browser, what would
          it look like? You wouldn’t just be able to pull in your favorite
          browser because it cannot exist as a part of the OpenCode monolith.
        </p><p>
          Similarly, what does a coding agent look like in the browser? Sure,
          you can use the appropriate agent SDKs from Codex, Claude Code
          (maybe?), OpenCode, etc. but what if you wanted to use T3 Code as the
          agent UI for this hypothetical browser integration? You wouldn’t,
          because T3 Code is a separate app with it’s own design goals and not
          part of the browser monolith.
        </p><p>
          One quote from that ancient Bret Victor
          <a href="https://worrydream.com/MagicInk/">paper</a> since for some
          reason I thought of bringing up a 20-year old paper of all things.
        </p><blockquote>
          <p>
            Monolithic systems are bad for software providers. In a healthy
            marketplace, whether of groceries or auto parts, individual
            providers offer components which combine with others for a complete
            solution. A small software provider could provide an excellent email
            program, or an excellent map. But only a large corporation has the
            resources to develop an integrated package. Once small companies
            can’t compete, progress stagnates.
          </p>
        </blockquote><p>
          Thinking about grocery stores for a second, of which there are many
          sellers within the store. While some big name food brands exist that
          are household names, many enjoyable ones aren’t. Furthermore, there’s
          no popular food brand in a grocery store that owns a monopoly on
          breakfast, lunch, or dinner. That is, you can make such meals by
          combining different food products from different brands. I don’t think
          the same can be said for software today, since each app wants to drag
          you into its own world entirely.
        </p><p>
          Is this to suggest that monolithic applications that have a more
          cohesive end-to-end experience by damned for eternity? Not
          necessarily, and if the universal app communication system struggles
          to model a cohesive interaction between 2 distinct applications, this
          will be necessary. However, if all the monolithic app does is provide
          a horizontal layout with a browser/editor tab next to an agent tab
          with no other visual interactions between the tabs, I wouldn’t exactly
          call that a cohesive end-to-end experience either.
        </p><p>
          I suppose, if there’s anything to build, it would be the universal
          communication mechanism. This is definitely something I’m interested
          in, but the sheer logistics of necessary adoption suggest that it will
          require more than just a single guy with agents and occassional free
          time.
        </p><p>
          Sure enough many (potentially millions) of us are in the same boat of
          having agents and occassional free time. It would be quite a waste if
          only a handful had the opportunity to be on the “winning team” that
          makes everyone else’s contributions irrelevant. We don’t need the next
          killer app made by a handful of people, we need the next killer effort
          contributed to by everyone.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Context-Free Grammar (CFG) Syntax Hell — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#context-free-grammar-cfg-syntax-hell</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#context-free-grammar-cfg-syntax-hell</guid>
      <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          CFGs are something I’ve been working with as of late, and I wanted to
          take moment to describe my suffering with all the various syntaxes and
          weird parsing behaviors.
        </p><p>
          First, for anyone getting into the topic of EBNF, just know that while
          there are some formal syntax definitions, the ecosystem of parsers is
          largely just imeplemented in a “freeform do whatever feels right” kind
          of sense. To this end, I’ve seen some parsers use ordered choice
          instead of unordered choice for alternation, PEG-style syntax, random
          regex/epsilon/character-class extensions. That is, there is literally
          no definition for what a valid EBNF syntax is, and the only thing I
          can say is that it depends on the parser you’re using.
        </p><p>
          In one sense, my idea of relativeness makes sense here. A parser
          should pick a format most optimized for its needs. On the other hand,
          we also have to ask if we need all of these syntax variants floating
          around? CFGs are a stable formalism that are a product of computer
          science research, so in this case I think it’s worth having a unified
          standard.
        </p><p>Some notes:</p><ul>
          <li>LLMs generally use GBNF to constrain outputs.</li>
          <li>The W3C standard seems to be pretty common.</li>
          <li>
            Almost no one uses Wirth syntax except for the Wikipedia article on
            it.
          </li>
          <li>ISO EBNF is also a pretty popular standard.</li>
          <li>Almost all parsers extend pure BNF in various ways.</li>
        </ul><p>
          I’m currently working on a Swift library to make grammar definitions
          easier and flexible. You’ll be able to define CFGs in a simple and
          standard formal syntax using strong types, proper formalisms of CFGs,
          result builders, and potentially macros.
        </p><pre><code class="lang-swift">
// Small Sneak Peak (not finalized)

<span class="hljs-keyword">let</span> grammar = Grammar(startingSymbol: .root) {
  Rule(.root) {
    Choice {
      Ref(<span class="hljs-string">"second"</span>)
      Ref(<span class="hljs-string">"first"</span>)
    }
  }

  Rule(<span class="hljs-string">"first"</span>) {
    <span class="hljs-string">"a"</span>
  }

  Rule(<span class="hljs-string">"second"</span>) {
    CharacterGroup(<span class="hljs-string">"A-Z0-9"</span>)
  }
}

<span class="hljs-keyword">let</span> language = Language {
  Union {
    Reverse {
      grammar
    }
    KleeneStar {
      otherGrammar
    }
    ConcatenateLanguages {
      l1
      l2
    }
  }
}
        </code></pre><p>
          Then you will be able to generate the syntax for whatever grammar spec
          you need, including upcoming CFG support in Cactus (which will be
          backed by XGrammar and GBNF). Note that since there are so many syntax
          variations, and tools that make up new syntax, I cannot realistically
          support everything. That being said, you can definitely expect a wide
          range of supported formats.
        </p><pre><code class="lang-swift">
<span class="hljs-comment">// More Sneak Peak (not finalized)</span>

<span class="hljs-keyword">let</span> formatted = <span class="hljs-keyword">try</span> grammar.formatted(style: .w<span class="hljs-number">3</span>cEbnf)
<span class="hljs-string">"""
root ::= second | first
first ::= "a"
second ::= [A-Z0-9]
"""</span>

<span class="hljs-keyword">let</span> formattedLanguage = <span class="hljs-keyword">try</span> language.grammar().formatted(style: .wirthEbnf)
<span class="hljs-comment">// GBNF is also supported at the time of writing this...</span>
<span class="hljs-comment">// ISO EBNF is also planned...</span>
<span class="hljs-comment">// So is a generic BNF format with many syntax options...</span>
        </code></pre><p>
          Additionally, the <code>formatted</code> function has to be throwing
          due to the disparity of expression support between different formats.
          The library tries to convert expressions for each built-in formatter
          if possible, but admittedly some expressions (eg. negated character
          groups in Wirth) cannot be converted. As a result, a formatter is
          allowed to throw if it encounters an unsupported expression.
        </p><p>
          Parsing is also something I would like to add in the future. However,
          it’s not a priority for release at the moment.
        </p><p>
          <strong>Update: </strong>
          <a href="https://www.grammarware.net/text/2012/bnf-was-here.pdf">https://www.grammarware.net/text/2012/bnf-was-here.pdf</a>
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>MiniMax — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#minimax</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#minimax</guid>
      <pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’m a Codex user, and GPT 5.4 is my go-to model for anything serious.
          That being said, it’s not good for everything, and there are times
          where simpler models that just get the job done are more ideal than
          Codex’s thouroughness. I suppose I could use Opus or Sonnet for this,
          but that would mean subscribing to Anthropic and using Claude Code.
          Unfortunately, I would much rather stay in OpenCode (or possibly a
          newer tool like T3 Code if it matures into something I like) to ensure
          that everything is as streamlined as possible with no weird TOS stuff
          in-between.
        </p><p>
          Since Gemini doesn’t seem to be optimized for agentic coding
          workflows, the only remaining options left are open weight models.
          Particularly, GLM, Kimi, Minimax, and Qwen have the edge in this
          space.
        </p><p>
          I haven’t bothered much with Qwen outside of using edge versions of it
          as a testing model for
          <a href="https://github.com/mhayes853/swift-cactus">swift-cactus</a>.
          Generally speaking, Qwen models aren’t generally that fast from my
          experience, at least on the Cactus engine. Furthermore, I don’t hear
          much talk about Qwen’s larger models overall, so I felt pulled in
          different directions.
        </p><p>
          That brings us to GLM 5 and Kimi K2.5 which have gotten a lot of
          attention. Having tried these models briefly, they both work very
          well, take their time, and can get most tasks done that codex can
          accomplish. However, I have Codex already, and I don’t need a cheaper
          and slightly worse overall version of it. Remember that I just want
          something that can perform simple things fast and well.
        </p><p>
          This leaves us with Minimax M2.5. While I wouldn’t say it’s output is
          as strong as GLM 5 and Kimi K2.5, it’s definitely not that far off,
          and can generally solve the tasks I need it for. Furthermore, it’s
          model size is quite small, only ~230B params compared to ~750B for GLM
          5 and ~1.1T for Kimit K2.5 by comparison, which means it’s often
          faster given equivalent hardware. The reason I like it is because it
          isn’t trying to be a cheap Codex or Opus, but rather be it’s own thing
          entirely. It has the exact opposite qualities compared to Codex, while
          still having good enough output quality, which is why I think it
          complements Codex very well.
        </p><p>
          Furthermore, from a subscriptions/usage standpoint, it’s also very
          generous if you go past their $10/month plan. I actually think that
          most people could seriously get by on their $20/month plan. Even when
          running it continuously through my recent entry into ralph-looping, I
          found that I never came even close to hitting any usage limits.
        </p><p>
          That being said, Minimax is no Codex or Opus, and you will have to
          significantly hand-hold the model, especially compared to Codex.
          Therefore, I still recommend the $200 or even $20 Codex tier above all
          else first, and an open-weight model like Minimax second. AFAIK, the
          $20 Codex tier is actually a lot better than the $20 Claude tier for
          many. I also think a $100 Codex tier would actually do quite well for
          many since it’s quite difficult to hit the limits on the $200 tier,
          which therefore means one is theoretically overspending on inference
          (not counting subsidization).
        </p><p>
          Another question one may ask is why not just get a subscription that
          gives you access to all the open-weight models like OpenCode or
          Synthetic? My reason for this is because OpenCode will give generally
          give free-access to open weight models for a limited time, which means
          that I will always get to at least try them without paying. Secondly,
          I’m not interested in the other openweight competition for now.
          Perhaps NVIDIA’s Nemotron model will change my mind, but for now I
          haven’t had any issues with Minimax itself.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Meaning Per Second — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#meaning-per-second</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#meaning-per-second</guid>
      <pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          When it comes to LLMs, we like to think of the idea of tokens per
          second almost as a measure of quality in many cases. I suppose this is
          similar to the focus on frames per second for graphics contexts.
          Though, I think what I’m noting here applies a more to the LLM case
          than it does the graphics case, it is applicable to the graphics case.
        </p><p>
          One interesting Alan Kay (or perhaps Xerox PARC) observation with
          regards to performance is the idea that the bar is the speed of the
          human nervous system. That is, as long as the human nervous system
          doesn’t notice delay, then all is well and good. For graphical
          contexts like those worked on at PARC, this makes a lot of sense.
          However, LLMs are also often paired with graphical contexts, and thus
          the human nervous system becomes the bar for speed yet again.
        </p><p>
          First, there are generally 2 kinds of speed metrics that we need to
          worry about for inference, prefill and decode. Prefill relates to
          forwarding tokens and building up the KV-cache, whereas decoding uses
          the KV-cache to generate output tokens. Generally speaking, decode is
          the more interesting measurement when we think about tokens per
          second, and especially so in the context of user interfaces.
          Prefilling can be hidden in the background for many applications, of
          which such background work can be used to significantly reduce latency
          to the first decoded token.
        </p><p>
          Secondly, we need to establish a base rate of speed for the human
          nervous system. Movies use 24 FPS as a baseline, but modern
          interactive user interfaces use 60-120 FPS. That being said, a
          user-interface is often still useable even if it dips slightly below
          that range, as long as the nervous system still perceives the
          interactivity as motion. If we use 60 FPS as a base rate, that leaves
          us about ~16.67ms between each frame.
        </p><p>
          Third, we need to consider what it means for a model to emit a token,
          and for a frame to be drawn to the screen. Each token or frame is
          generally used to build up a larger communication of some kind, such
          as the words of an essay or the strokes of a drawing. Of course, the
          most important substantial transfer in any communication is
          <em>meaning</em>. Without being able to convey the meaning of
          something, communications become misunderstood.
        </p><p>
          What this all means is that we have ~16.67ms to emit meaning in any
          given scenario. Translate that into 60 TPS, and we’ll see that such a
          speed is already relatively common for LLMs today. Therefore, we
          already have the means of beating the nervous system from a raw
          throughput perspective.
        </p><p>
          However, let’s take a moment to note the difference in output between
          graphics and LLMs. LLMs generally emit text, where as graphics emit
          pictures. This creates another bottleneck for LLMs, because human
          minds absorb meaning from pictures much faster than words. Today’s
          diffusion models are of course obviously not up to that level of
          speed, and it’s likely they won’t be for another few iterations of
          Moore’s Law (Perhaps one can buy their way into the future here
          similarly to Xerox PARC).
        </p><p>
          Therefore, if we want to communicate in pictures today using an LLM,
          our best approach from an engineering standpoint is to translate the
          output tokens into pictures. However, once we start thinking in terms
          of pictures, we stop thinking in terms of TPS, but rather a rate of
          meaning per token. That is, how much can a token translate to the
          right picture? Further, we only have ~16.67ms to do so as a base rate.
        </p><p>
          So we can see that changing the medium of communication itself brings
          us different design and necessary throughput constraints. Pictures as
          a medium have a higher throughput than words for certain kinds of
          meaning, but words often win when it comes to precise formal meaning.
          Regardless, if the point of all of this is optimization, then perhaps
          “meaning per second” should be the optimization slogan.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>I Actually Tried A Ralph Loop — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#i-actually-tried-a-ralph-loop</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#i-actually-tried-a-ralph-loop</guid>
      <pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          After 2 months of seriously using agents, I finally felt comfortable
          trying a HIL version of Ralph on a recent internal tool to do some
          marketing analysis for my company’s
          <a href="https://stresyx.com/">pivot</a>. Also, I needed a test drive
          for my latest and quite big release of swift-cactus 2.0. (I’ll write
          something formal about this another time, CFG support in the main
          engine is still needed to get it where it needs to be…)
        </p><p>
          Additionally, a model I’ve been playing around with quite a lot
          recently is Minimax M2.5. A TLDR for why I like it is that it’s not
          trying to be a cheap Codex or Opus like GLM and Kimi, and I wanted
          something that wasn’t Codex for certain tasks. Regardless, this was
          the model I decided to use for the sake of doing so.
        </p><p>
          The tool itself was a straightforward CLI to fetch some posts from
          various data sources (Reddit primarily), and feed the content into
          LFM2-8b-a1b running locally via the cactus engine to produce
          suggestions and a report regarding the validity of a user defined
          hypothesis. Additionally, qwen3-embed-0.6b was also used as an
          embedding model for both vector indexing and aiding with
          categorization for posts. I also used this chance to play with
          <a href="https://github.com/christopherkarani/Wax">Wax</a>, a
          single-file vector database written in pure Swift, and was the primary
          persistence mechanism of choice. Also if it wasn’t obvious, Swift was
          used as the programming language.
        </p><p>
          Overall, the task was completed with about ~4-5 hours of HIL ralphing,
          though improvements can certainly still be made to the experience of
          the tool itself. Therefore, the experiment was certainly a success
          from the standpoint of being able to produce something that is
          functional.
        </p><p>
          My overall idea itself was to start in one session by creating a plan
          and detailed implementation specification document with the agent.
          This specification was one large markdown file because I wasn’t trying
          to build something incredibly complicated. Then, I ran another agent
          session which broke down that implementation spec into 17 distinct
          tasks listed in another document. Each task included, a title, a
          description, completion criteria, and a list of tasks it depended on.
          Since we are doing Ralph, the agent got to pick the order of task
          completion. (It went mostly sequential with a slight exception towards
          the later tasks where it actually backtracked for a bit.)
        </p><p>
          Of course, my thoughts on general software development techniques are
          generally mixed, and Ralph is no exception to this rule. Generally
          speaking, over dogmatic focus on patterns instead of systems is how
          you get complexity, so we have to keep that in mind at the end of the
          day. Nevertheless, here’s a somewhat comprehensive list that reflects
          my experience.
        </p><ul>
          <li>
            The code quality was absolutely terrible.
            <ul>
              <li>
                Before listing individual cases here, I should mention that this
                is a tool that will be thrown away in a few weeks time, so the
                quality isn’t that important.
              </li>
              <li><code>@unchecked Sendable</code> everywhere.</li>
              <li>
                Loading the model weights from scratch every time embeddings or
                inference was needed, instead of keeping the weights in memory.
              </li>
              <li>
                Weird Java-like patterns that don’t make sense in Swift.
                <ul>
                  <li>Getter/Setter methods was common for some reason.</li>
                </ul>
              </li>
              <li>
                Writing tests for obvious things, like compiler-synthesized
                Codable conformances.
              </li>
              <li>
                Not using actors properly, and preferring NSLock when an actor
                would make things simpler.
                <ul>
                  <li>
                    In one case, it tried to use a serial dispatch queues within
                    the actor itself to serialize work. Even worse, it would use
                    <code>queue.async</code>, and await values via
                    <code>withUnsafeContinuation</code>. I’m not making this up…
                  </li>
                </ul>
              </li>
              <li>
                Coupling generic logic with domain logic.
                <ul>
                  <li>
                    In one case, it wrote a cosine similarity method that was
                    private in a class. Generally speaking, I always try to
                    extract such generic methods into a reusable place even if
                    they are only used once.
                  </li>
                </ul>
              </li>
              <li>Etc.</li>
            </ul>
          </li>
          <li>
            It did a decent job separating interface from implementation via
            protocols.
            <ul>
              <li>
                That is, it was able to create mocks of things for testing which
                was nice.
              </li>
              <li>
                While it did create protocols, it often named/coupled the
                protocol requirements to the implementation itself which isn’t
                ideal.
              </li>
            </ul>
          </li>
          <li>
            It completed the work in a very short period of time.
            <ul>
              <li>
                ~8k LOC in 4-5 hours of work isn’t bad, the same amount of code
                would’ve taken me at least a solid week of dedicated effort.
              </li>
            </ul>
          </li>
          <li>
            It did a decent job of implementing the isolated and mundane things.
            <ul>
              <li>Reddit API Client</li>
              <li>Wax memory wrapper</li>
              <li>Cactus embedding provider for Wax</li>
              <li>Domain model types/simple structs</li>
              <li>Persistence</li>
              <li>Configuration reading</li>
              <li>Etc.</li>
            </ul>
          </li>
          <li>
            It did a terrible job of implementing the main tool loop.
            <ul>
              <li>
                For some reason, it would avoid actually writing the part where
                the LLM was called in the main loop of the tool.
              </li>
              <li>
                Additionally, it avoided integrating in all of the isolated
                modules in the code base, preferring to use mocks in the
                production code.
              </li>
              <li>
                Suffice to say, I had to break HIL Ralph and take over manually
                via normal agentic coding to get it to actually build the main
                loop of the tool.
              </li>
            </ul>
          </li>
        </ul><p>What did we learn here?</p><p>
          For one, I could probably be a better spec writer, and use Codex
          instead of Minimax. Also, I’m sure with more practice the overall
          output will become better regardless of the model choice. What I did
          certainly find was that many trivial modules can easily be ralphed
          with very little effort, however the fun parts of building are where
          breaking out of the loop seems to be a better idea.
        </p><p>
          Ralph gives more control to the agent than you. In normal agentic
          coding, you can generally get the agent to write decent code if you
          direct it well. (Though it will still often miss critical performance
          details, and make ~2-3 small mistakes per 1000 lines). However, the
          quality seems to go way down when you hand full control to the agent.
          This is ok if you limit its crappy output to a series of well-defined
          interfaces in your spec, so make sure you nail your higher level
          design decisions.
        </p><p>
          Overall, I once again think of this as a technique that’s great in the
          sense of graphic design to art. That is, it can get stuff done like a
          graphic designer, but it lacks the ability to produce incredible art.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Non-Sendable Core, Sendable Shell</title>
      <link>https://whypeople.xyz/non-sendable-core-sendable-shell</link>
      <guid isPermaLink="true">https://whypeople.xyz/non-sendable-core-sendable-shell</guid>
      <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
      <description>How to push Swift Concurrency to the edge, and make the rest of your code simpler.</description>
      <content:encoded><![CDATA[<h1 id="non-sendable-core-sendable-shell" class="title">
          Non-Sendable Core, Sendable Shell
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>
        <p>
          Swift Concurrency seems to be one of the hard Swift topics, and one
          thing we can do in Computer Science is to continue pushing hard
          problems away to another layer of abstraction. Naturally, we can do
          that with Swift Concurrency too!
        </p>
        <p>
          This isn’t an article about understanding isolation or Sendable. I
          assume you already are somewhat familiar with basic Swift Concurrency
          concepts. Rather, I more or less want to outline the pain points
          around those concepts, and demonstrate a technique for pushing them to
          the edge.
        </p>
        <br>
        <h2 id="tldr-example">TLDR Example</h2>
        <p>
          Since this article naturally draws out the explanation to include many
          details, I’ll give a TLDR code example right away. That is,
          non-Sendable types are much more flexible than Sendable types.
          Sendable types require either locking or actor isolation overhead, so
          it’s much easier to defer such mechanisms as long as possible.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// ✅ By making CactusModel non-Sendable, we can reuse it </span>
<span class="hljs-comment">// in any Sendable type whether it's an actor, or class with a lock.</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CactusModel</span>: ~<span class="hljs-title">Copyable</span> </span>{
  <span class="hljs-comment">// Non-Sendable Core...</span>
}

<span class="hljs-keyword">final</span> actor <span class="hljs-type">CactusModelActor</span> {
  <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusModel</span>

  <span class="hljs-keyword">init</span>(model: consuming sending <span class="hljs-type">CactusModel</span>) {
    <span class="hljs-keyword">self</span>.model = model
  }

  <span class="hljs-comment">// Sendable Shell...</span>
}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SomeState</span>: <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">State</span> </span>{
    <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusModel</span>
    <span class="hljs-keyword">var</span> otherState: <span class="hljs-type">Value</span>
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> state = <span class="hljs-type">Mutex</span>(<span class="hljs-type">State</span>())

  <span class="hljs-comment">// Sendable Shell...</span>
}
      </code></pre>
        <p>
          With that out of the way, now we can focus on dragging out the
          explanation from first principles. However, I would first like to look
          at a corollary idea, and where this article gets its naming scheme
          from.
        </p>
        <br>
        <h2 id="functional-core-imperative-shell">
          Functional Core, Imperative Shell
        </h2>
        <p>
          The name of this article comes from the “Functional Core, Imperative
          Shell” idea. If you’re unfamiliar with “Functional Core, Imperative
          Shell”, here’s a simple code example to illustrate.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Item</span> </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// ❌ Bad, mixes IO (side effects) and business logic.</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processBad</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">let</span> items = <span class="hljs-keyword">try</span> await loadItems()
  <span class="hljs-keyword">let</span> transformed = items.<span class="hljs-built_in">filter</span> { <span class="hljs-comment">/* ... */</span> }
    .<span class="hljs-built_in">map</span> { <span class="hljs-comment">/* ... */</span> }
    .reversed()
    .<span class="hljs-built_in">dropFirst</span>()
  <span class="hljs-keyword">try</span> await save(transformed)
}

<span class="hljs-comment">// ✅ Good, separates IO (side effect) from business logic</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processGood</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">let</span> items = <span class="hljs-keyword">try</span> await loadItems()
  <span class="hljs-keyword">try</span> await save(items.transformed)
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Sequence</span> <span class="hljs-title">where</span> <span class="hljs-title">Element</span>: <span class="hljs-title">Item</span> </span>{
  <span class="hljs-keyword">var</span> transformed: some <span class="hljs-type">Sequence</span>&lt;<span class="hljs-type">TransformedItem</span>&gt; {
    items.<span class="hljs-built_in">filter</span> { <span class="hljs-comment">/* ... */</span> }
      .<span class="hljs-built_in">map</span> { <span class="hljs-comment">/* ... */</span> }
      .reversed()
      .<span class="hljs-built_in">dropFirst</span>()
  }
}
      </code></pre>
        <p>
          By separating out the collection transformation, we gain more
          flexibility. That is, we can choose to reuse the transformation logic
          regardless of how the underlying data is persisted, or even write
          isolated unit tests just for the transformation logic. In general,
          once IO or side effects are introduced to your code, it becomes much
          less flexible, so it's best to keep those isolated from the core to
          maintain flexibility.
        </p>
        <p>
          In the above example, the transformation logic is what we call the
          “functional core”, and the IO is what we call the “imperative shell”.
        </p>
        <br>
        <h2 id="async-makes-things-harder">Async Makes Things Harder</h2>
        <p>
          Generally speaking, once async/await is required to call a method,
          things get much harder. If you’re in a synchronous context, you’ll
          either have to create an unstructured task, or mark the enclosing
          method as <code>async</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span></span> {
  <span class="hljs-comment">// ❌</span>
  await someWork()
}

<span class="hljs-comment">// ✅ Either approach compiles, but aren't always ideal</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span> <span class="hljs-title">async</span></span> {
  <span class="hljs-comment">// 🔵 Delegates async responsibility to whoever calls foo</span>
  await someWork()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span></span> {
  <span class="hljs-comment">// 🔵 Completely hides the fact that async unstructured work has been started</span>
  Task { await someWork() }
}
      </code></pre>
        <p>
          Furthermore, you open a whole can of worms regarding
          <code>Sendable</code> once you start getting into concurrency land.
          When it comes to dealing with non-Sendable types, you have to take
          special care when passing them across actor isolation boundaries,
          otherwise the Swift compiler will hate you.
        </p>
        <p>
          Thankfully, the <code>sending</code> keyword can help somewhat when it
          comes to non-Sendable types and tasks. This keyword allows you to
          transfer non-Sendable values across isolation boundaries, which means
          that you can use non-Sendable values within tasks.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">(value: sending NonSendable)</span></span> {
  <span class="hljs-type">Task</span> {
    <span class="hljs-comment">// ✅ value is being explicitly transferred into the Task's </span>
    <span class="hljs-comment">// isolation context</span>
    <span class="hljs-keyword">let</span> v = value
  }
}
      </code></pre>
        <p>
          However, it doesn't work everywhere. For instance, you can’t use
          <code>sending</code> to capture a non-Sendable value inside an
          <code>@Sendable</code> closure.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">badTransfer</span><span class="hljs-params">(
  value: sending NonSendableValue
)</span> -&gt; @<span class="hljs-title">Sendable</span> <span class="hljs-params">()</span> -&gt; <span class="hljs-title">Void</span></span> {
  <span class="hljs-comment">// ❌ Capture of 'value' with non-Sendable type 'NonSendableValue' in a '@Sendable' closure</span>
  <span class="hljs-keyword">return</span> { work(value) }
}
      </code></pre>
        <p>
          The reason this doesn’t work is because
          <code>@Sendable</code> closures are meant to be thread-safe, and as a
          result may be called multiple times from different threads. Once you
          understand that fact, it becomes clear why the above code example
          doesn’t work. The point of the <code>sending</code> keyword is only to
          send a value to a different isolation context, and nothing more. In
          fact, the original proposal for <code>sending</code> actually named
          the keyword <code>transferring</code> at first until it was renamed
          presumably due to naming conflation with Swift's ownership model and
          to be more in line with Sendable.
        </p>
        <br>
        <h2 id="conforming-to-sendable-can-be-annoying">
          Conforming to Sendable can be Annoying
        </h2>
        <p>
          If we can’t use the <code>sending</code> keyword to get around
          concurrency issues, the next step up is to conform to Sendable
          directly. For simple structs, this is trivial because the compiler can
          infer thread-safety for you. In fact, I personally do this by
          <em>default</em> whenever I make a new simple struct data type.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// ✅ Trivially Sendable.</span>
<span class="hljs-comment">//</span>
<span class="hljs-comment">// I always tend to conform simple structs to Hashable and Sendable </span>
<span class="hljs-comment">// without second thought.</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Item</span></span>: Hashable, Sendable {
  <span class="hljs-keyword">let</span> fieldA: Int
  <span class="hljs-keyword">let</span> fieldB: <span class="hljs-built_in">String</span>
}
      </code></pre>
        <p>
          However, things get harder when we need to deal with reference types.
          Any non-final class, or even simple mutable properties will prevent an
          auto synthesized Sendable conformance.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// ❌ Cannot have mutable properties</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Item</span>: <span class="hljs-type">Sendable {</span></span>
  <span class="hljs-keyword">var</span> fieldA: <span class="hljs-built_in">Int</span>
  <span class="hljs-keyword">var</span> fieldB: String
}

<span class="hljs-comment">// ❌ Must be a final class </span>
<span class="hljs-comment">// (Subclasses could add mutable/non-Sendable properties otherwise)</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Item</span>: <span class="hljs-type">Sendable {</span></span>
  let fieldA: <span class="hljs-built_in">Int</span>
  let fieldB: String
}
      </code></pre>
        <h3 id="unchecked-sendable">Unchecked Sendable</h3>
        <p>
          Of course, you can use <code>@unchecked Sendable</code>, but that
          usually isn’t something you want to deliberately touch because it
          turns off compiler checking for Sendable. That is, the compiler can no
          longer offer any guarantees that your type is thread-safe. Think of it
          in the same vein as force-unwrapping an optional. It’s allowed, but
          you better know what you’re doing.
        </p>
        <p>
          I’m not suggesting that you should never use
          <code>@unchecked Sendable</code>. In fact, if you go deep enough with
          integrating legacy frameworks, low-level C libraries, or locks, it’s
          almost certain that you will need to use it at some point. Just make
          sure you justify yourself when you do so, ideally through a comment
          next to the <code>@unchecked Sendable</code> conformance.
        </p>
        <h3 id="actors">Actors</h3>
        <p>
          Another way to get automatic an Sendable conformance is to convert
          your type to an actor (or just throw a global actor like
          <code>@MainActor</code> at the problem).
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// ✅ Actors are Sendable by default.</span>

actor Item {
  <span class="hljs-keyword">var</span> fieldA: Int
  <span class="hljs-keyword">var</span> fieldB: <span class="hljs-built_in">String</span>
}

<span class="hljs-meta">@MainActor</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Item</span> </span>{
  <span class="hljs-keyword">var</span> fieldA: Int
  <span class="hljs-keyword">var</span> fieldB: <span class="hljs-built_in">String</span>
}
      </code></pre>
        <p>
          For <code>@MainActor</code> specifically, new Xcode projects even make
          MainActor isolation the default for all declarations. However, if you
          want a type be usable from multiple isolation contexts, you will have
          to mark it <code>nonisolated</code> in addition to a Sendable
          conformance.
        </p>
        <pre><code class="lang-swift">
nonisolated struct Item: Sendable {
  <span class="hljs-selector-tag">var</span> fieldA: Int
  <span class="hljs-selector-tag">var</span> fieldB: String
}
      </code></pre>
        <p>
          On a personal note, I tend not to use default MainActor isolation. At
          least for me, I like to be explicit when something is main actor
          compatible, and since I tend to write a lot of thread-agnostic non-UI
          code in addition UI code, I find it simpler to keep everything
          consistent and turn off default Main Actor isolation.
        </p>
        <p>
          Regardless of how you choose to use actors, you’ll be dealing with
          async/await when you’re not isolated to the actor. As previously
          mentioned above, adding async just complicates things.
        </p>
        <p>
          With actors, there’s also more lesser-understood points of conceptual
          overhead.
        </p>
        <ul>
          <li>
            The <code>SerialExecutor</code> protocol allows an actor to
            customize its execution environment.
            <ul>
              <li>Useful if you need to run things on a dedicated thread.</li>
            </ul>
          </li>
          <li>
            Any non-Sendable type returned from an actor method also requires
            <code>sending</code>
            <ul>
              <li>
                Furthermore, the value returned from the method also cannot be
                an instance member of the actor, because doing so would make the
                non-Sendable type present in 2 separate isolation contexts.
              </li>
            </ul>
          </li>
          <li>
            Actor interleaving.
            <ul>
              <li>
                Only 1 task can enter an actor at a time, so if a task must
                await another <code>async</code> method within the actor, it
                must leave the current actor to swap isolation contexts. This
                allows another task to enter[^1] the actor in the meantime.
              </li>
            </ul>
          </li>
          <li>
            Actor re-entrancy.
            <ul>
              <li>
                Once the original task from the interleaving explanation is done
                performing its async work, it must re-enter the original actor
                that it once left to continue execution past the suspension
                point it previously awaited.
              </li>
            </ul>
          </li>
          <li>
            <code>isolated</code> parameters.
            <ul>
              <li>
                This allows one to declare methods external to the actor as
                being isolated to the actor. Since a task can only be on at most
                1 actor at a time, this also means that a function can only have
                at most 1 isolated parameter.
              </li>
            </ul>
          </li>
        </ul>
        <br>
        <h2 id="avoiding-sendable">Avoiding Sendable</h2>
        <p>
          Since Sendable brings a lot of inconveniences, we have to ask
          ourselves the question of how long it can be avoided. In today’s
          world, the answer to this that we can get quite far with non-Sendable
          types.
        </p>
        <h3 id="async-methods-in-non-sendable-types">
          Async Methods in Non-Sendable Types
        </h3>
        <p>
          One of the previous issues with non-Sendable types was the notion of
          having async methods on them. Invoking them would cause an isolation
          hop to a non-isolated context, which would produce often very
          non-intuitive errors.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NonSendable</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span> async {
    <span class="hljs-comment">// ...</span>
  }
}

@<span class="hljs-type">MainActor</span>
@<span class="hljs-type">Observable</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyViewModel</span> </span>{
  <span class="hljs-keyword">let</span> nonSendable = <span class="hljs-type">NonSendable</span>()

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">buttonTapped</span><span class="hljs-params">()</span></span> async {
    <span class="hljs-comment">// ❌ Sending 'self.nonSendable' risks causing data races</span>
    await <span class="hljs-keyword">self</span>.nonSendable.work()
  }
}
      </code></pre>
        <p>
          However, today you can use <code>nonisolated(nonsending)</code> to fix
          this error.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NonSendable</span> </span>{
  nonisolated(nonsending) <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span> async {
    <span class="hljs-comment">// ...</span>
  }
}

@<span class="hljs-type">MainActor</span>
@<span class="hljs-type">Observable</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyViewModel</span> </span>{
  <span class="hljs-keyword">let</span> nonSendable = <span class="hljs-type">NonSendable</span>()

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">buttonTapped</span><span class="hljs-params">()</span></span> async {
    <span class="hljs-comment">// ✅ No longer sending non-Sendable to a nonisolated context</span>
    await <span class="hljs-keyword">self</span>.nonSendable.work()
  }
}
      </code></pre>
        <p>
          What this tells the compiler is that <code>work</code> will execute on
          the isolation context that it is invoked on. In fact, you can even
          enable this behavior by default through the
          <code>NonIsolatedNonSendingByDefault</code> feature.
        </p>
        <p>
          However, it's also worth noting that this theoretical example is a
          textbook definition of “NonSendable core, Sendable Shell” since the
          <code>NonSendable</code> class acts as the non-Sendable core and
          <code>MyViewModel</code> the Main Actor bound Sendable shell. Now, we
          can look at some more practical examples involving wrapping a C
          library that I’m involved in semi-regularly.
        </p>
        <br>
        <h2 id="practical-example-swift-cactus">
          Practical Example: Swift Cactus
        </h2>
        <p>
          In particular, say you need to wrap a C library with weird threading
          semantics, but need to integrate it with Swift Concurrency. For this
          example, I’ll wrap the
          <a href="https://github.com/cactus-compute/cactus">cactus inference engine</a>, which is a highly energy efficient, ARM optimized, and performant
          alternative to MLX, ONNX, llama, etc. that runs primarily on the CPU.
          I’m not going to make a sales pitch for the engine in this article
          because I want the focus to be wrapping it properly from Swift. In
          fact, the principles in these code examples can be found in the
          <a href="https://github.com/mhayes853/swift-cactus">swift-cactus</a>
          package that I actively maintain.
        </p>
        <p>
          The Cactus FFI exposes many functions, so I’ll only include heavily
          simplified signatures of some of the basic FFI functions for
          simplicity. Just note that in reality the library supports many more
          advanced features, but the underlying principles of the Swift API
          design are the same.
        </p>
        <pre><code class="lang-c">
<span class="hljs-comment">// Loads model weights.</span>
<span class="hljs-function">CACTUS_FFI_EXPORT cactus_model_t <span class="hljs-title">cactus_init</span><span class="hljs-params">(
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* model_path
)</span></span>;

<span class="hljs-comment">// Deallocate a model.</span>
<span class="hljs-function">CACTUS_FFI_EXPORT <span class="hljs-keyword">void</span> <span class="hljs-title">cactus_destroy</span><span class="hljs-params">(<span class="hljs-keyword">cactus_model_t</span> model)</span></span>;

<span class="hljs-comment">// Stops an active ongoing generation for a model.</span>
<span class="hljs-function">CACTUS_FFI_EXPORT <span class="hljs-keyword">void</span> <span class="hljs-title">cactus_stop</span><span class="hljs-params">(<span class="hljs-keyword">cactus_model_t</span> model)</span></span>;

<span class="hljs-comment">// Runs inference for a given model, and outputs the </span>
<span class="hljs-comment">// result into response_buffer.</span>
<span class="hljs-function">CACTUS_FFI_EXPORT <span class="hljs-keyword">int</span> <span class="hljs-title">cactus_complete</span><span class="hljs-params">(
    <span class="hljs-keyword">cactus_model_t</span> model,
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* messages_json,
    <span class="hljs-keyword">char</span>* response_buffer,
    <span class="hljs-keyword">size_t</span> buffer_size
)</span></span>;
      </code></pre>
        <p>
          <code>cactus_model_t</code> itself is an opaque pointer, but under the
          hood it’s represented as a struct in C++ that looks something like
          this. (Once again, very simplified from the actual handle[^2].)
        </p>
        <pre><code class="lang-cpp">
struct CactusModelHandle {
    <span class="hljs-symbol">std:</span><span class="hljs-symbol">:unique_ptr&lt;cactus</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:engine</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:Model&gt;</span> model;
    <span class="hljs-symbol">std:</span><span class="hljs-symbol">:atomic&lt;bool&gt;</span> should_stop;

    CactusModelHandle() : should_stop(<span class="hljs-keyword">false</span>) {}
};
      </code></pre>
        <p>
          As we can see, we have an atomic for whether or not ongoing inference
          should stop. Furthermore, the <code>Model</code> class in the C++
          engine does not use any locks under the hood. This puts us in a weird
          case where <code>cactus_stop</code> is safe to call from different
          isolation contexts in Swift, but <code>cactus_complete</code> is not.
        </p>
        <p>
          However, LLM inference is also incredibly expensive, and certainly you
          should not be doing it from the Main Actor. Yet, only exposing the
          model API behind an actor would limit the Swifty wrapper to purely
          async contexts.
        </p>
        <p>
          As such, I think this is a perfect case for non-Sendable core,
          Sendable shell. Since we’re wrapping a pointer from an underlying C
          library, we can use a non-Sendable and non-Copyable struct to ensure
          ownership semantics when passing the model around. Then, we can create
          a wrapper <code>CactusModelActor</code> that wraps our non-Copyable
          type to grant proper built-in Sendable and background execution to the
          inference engine.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CactusModel</span>: ~<span class="hljs-title">Copyable</span> </span>{
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> pointer: cactus_model_t

  <span class="hljs-keyword">init</span>(pointer: consuming sending cactus_model_t) {
    <span class="hljs-keyword">self</span>.pointer = pointer
  }

  <span class="hljs-keyword">init</span>(path: <span class="hljs-type">URL</span>) {
    <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> pointer = cactus_init(path.absoluteString) <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-type">CactusModelError</span>.failedToLoadModel
    }
    <span class="hljs-keyword">self</span>.<span class="hljs-keyword">init</span>(pointer: pointer)
  }

  <span class="hljs-keyword">deinit</span> {
    cactus_destroy(<span class="hljs-keyword">self</span>.pointer)
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">complete</span><span class="hljs-params">(messages: [Message])</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Completion</span> {
    <span class="hljs-keyword">let</span> encodedMessages = <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(messages)
    <span class="hljs-keyword">let</span> buffer = <span class="hljs-type">UnsafeMutableBufferPointer</span>&lt;<span class="hljs-type">CChar</span>&gt;.allocate(capacity: <span class="hljs-number">8192</span>)
    <span class="hljs-keyword">defer</span> { buffer.deallocate() }
    <span class="hljs-keyword">let</span> result = cactus_complete(
      <span class="hljs-keyword">self</span>.pointer,
      <span class="hljs-type">String</span>(decoding: encodedMessages, <span class="hljs-keyword">as</span>: <span class="hljs-type">UTF8</span>.<span class="hljs-keyword">self</span>),
      buffer,
      <span class="hljs-number">8192</span> * <span class="hljs-type">MemoryLayout</span>&lt;<span class="hljs-type">CChar</span>&gt;.<span class="hljs-built_in">stride</span>
    )
    <span class="hljs-keyword">if</span> result &lt; <span class="hljs-number">0</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-type">CactusModelError</span>.badResponse
    }
    <span class="hljs-keyword">let</span> data = bufferToData(buffer)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">ChatCompletion</span>.<span class="hljs-keyword">self</span>, from: data)
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span></span> {
    cactus_stop(<span class="hljs-keyword">self</span>.pointer)
  }
}

actor <span class="hljs-type">CactusModelActor</span> {
  <span class="hljs-keyword">private</span> actor <span class="hljs-type">DefaultIsolation</span> {}

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> defaultIsolation = <span class="hljs-type">DefaultIsolation</span>()
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> executor: (any <span class="hljs-type">SerialExecutor</span>)?
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusModel</span>

  nonisolated <span class="hljs-keyword">var</span> unownedSerialExeuctor: <span class="hljs-type">UnownedSerialExecutor</span> {
    <span class="hljs-keyword">self</span>.executor?.asUnownedSerialExecutor() ?? <span class="hljs-keyword">self</span>.defaultIsolation.unownedSerialExecutor
  }

  <span class="hljs-keyword">init</span>(
    executor: (any <span class="hljs-type">SerialExecutor</span>)? = <span class="hljs-literal">nil</span>,
    model: consuming sending <span class="hljs-type">CactusModel</span>
  ) {
    <span class="hljs-keyword">self</span>.executor = executor
    <span class="hljs-keyword">self</span>.model = model
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">complete</span><span class="hljs-params">(messages: [Message])</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Completion</span> {
    <span class="hljs-keyword">try</span> <span class="hljs-keyword">self</span>.model.complete(messages: messages)
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">self</span>.model.stop()
  }

  borrowing <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withModel</span>&lt;T, E: Error&gt;<span class="hljs-params">(
    operation: <span class="hljs-params">(CactusModel)</span></span></span> <span class="hljs-keyword">throws</span>(<span class="hljs-type">E</span>) -&gt; sending <span class="hljs-type">T</span>
  ) <span class="hljs-keyword">throws</span>(<span class="hljs-type">E</span>) -&gt; sending <span class="hljs-type">T</span> {
    <span class="hljs-keyword">try</span> operation(<span class="hljs-keyword">self</span>.model)
  }
}
      </code></pre>
        <p>
          As you can see, the actor provides quite a few concurrency specific
          additions, such as even allowing a custom
          <code>SerialExecutor</code> to be used. This latter bit could be
          useful for an application because the inference engine will block the
          cooperative thread pool for a <em>long</em> time. A custom executor
          allows you to create a dedicated thread outside of the cooperative
          thread-pool for inference in your application.
        </p>
        <p>
          However, because we split the non-Sendable core from the Sendable
          shell, we could also decide to use the model inside another completely
          synchronous context. This could include a class with a lock, a
          non-main global actor, or another actor that bundles various
          non-Sendable types together. If our needs are simple enough, we could
          even just rely on region based isolation with <code>sending</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LockedModel</span>: <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-keyword">let</span> model: <span class="hljs-type">Mutex</span>&lt;<span class="hljs-type">CactusModel</span>&gt;

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

<span class="hljs-keyword">final</span> actor <span class="hljs-type">CactusModelAgent</span> {
  <span class="hljs-keyword">var</span> transcript: [<span class="hljs-type">Message</span>]
  <span class="hljs-keyword">var</span> nonSendable: <span class="hljs-type">NonSendable</span>
  <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusModel</span>

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

@<span class="hljs-type">NonMainGlobalActor</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModelActor</span> </span>{
  <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusModel</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">infer</span><span class="hljs-params">(model: sending CactusModel)</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-comment">// ...</span>
}
      </code></pre>
        <p>
          Of course, it is also possible to use
          <code>CactusModel</code> irresponsibly like this.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-variable">@MainActor</span>
<span class="hljs-variable">@Observable</span>
final class Store {
  <span class="hljs-comment">// ❌ Really bad idea, inference is insanely expensive and should not be run on the Main Actor</span>
  <span class="hljs-selector-tag">let</span> <span class="hljs-selector-tag">model</span>: <span class="hljs-selector-tag">CactusModel</span>
}
      </code></pre>
        <p>
          However, the flexibility out weights the potential cons of doing
          something not so bright. Even if someone were to attempt to do
          inference on the main actor, we could always check if the caller is on
          the main actor and emit a purple runtime warning discouraging that
          behavior using a library such as
          <a href="https://github.com/pointfreeco/swift-issue-reporting">IssueReporting</a>.
        </p>
        <br>
        <h2 id="conclusion">Conclusion</h2>
        <p>
          Non-Sendable types are actually quite nice to work with. You don’t
          have to worry about thread-safety, isolation, or any other weird
          cryptic errors. Even <code>nonisolated(nonsending)</code> will
          eventually become the default for all <code>async</code> functions,
          but you can enable default behavior for it today through the
          <code>NonIsolatedNonSendingByDefault</code> feature.
        </p>
        <p>
          Further, when you focus on making things non-Sendable, you gain more
          control over how you want to handle the concurrency behavior for that
          logic. Whether that’s through local actor isolation,
          <code>@MainActor</code>/global actor isolation, a class with a lock,
          or even just through region based isolation with <code>sending</code>,
          you get that flexibility.
        </p>
        <p>
          At the end of the day, concurrency is difficult, and it’s why we have
          tools like Swift Concurrency in the first place. That being said, the
          simplest form of concurrency is often no concurrency, so I would try
          to stay in that land for as long as possible.
        </p>
        <p>— 2/25/26</p>
        <p>
          [^1]: I find it's best to think of actor interleaving in the sense
          that tasks enter actors rather than actors executing tasks. If the
          task must enter an actor, then it can therefore only be on 1 actor at
          a time, just like how a human can only be in 1 physical location at
          any given time. In order to hop to another isolation context, the task
          therefore must leave the actor, just as a human must leave their
          current location to go somewhere else. When speaking in these terms,
          ideas like actor-interleaving and re-entrancy make a lot more sense.
        </p>
        <p>
          [^2]: At the time of writing this, if you look at the actual source
          code for the engine, you will find a <code>model_mutex</code> inside
          the handle struct. However, it isn’t actually used in
          <code>cactus_complete</code>. As a result, the model pointer still
          must be treated as non-thread-safe in higher level code that calls
          into the inference engine.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Why I don’t (and likely never will) use Claude Code. — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#why-i-don-t-and-likely-never-will-use-claude-code-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#why-i-don-t-and-likely-never-will-use-claude-code-</guid>
      <pubDate>Wed, 18 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          <img src="./global-notes/assets/why-i-wont-use-claude-code.png" alt="An image displaying Claude Code's terms of service in which they forbid the use of OAuth credentials for any product, tool, or service (including the Agent SDK) that isn't Claude Code or Claude.ai.">
          There will be many new tools that come in the future around agents,
          many will be better than Claude Code, and almost certainly we need
          better tools than Claude Code. I have no interest in being shackled
          when those tools are created.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Are Apps Dead? — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#are-apps-dead-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#are-apps-dead-</guid>
      <pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you’ve been paying attention to Peter Steinberger and the
          commentary around OpenClaw, a common trope is that most (80%) if not
          all apps are supposedly dead. For the record, Peter Steinberger comes
          from the iOS world himself, which I suppose counts as credibility
          here.
        </p><p>
          In my opinion, as someone who also does app development as a career,
          he’s kind of right if we’re referring to the state of mobile apps
          today. Most apps that just display simple information from an API are
          usually not the hardest things to create, and their UIs are genuinely
          not very inspiring. These kinds of apps can be merged into something
          like OpenClaw through a good API layer.
        </p><p>
          That being said, we have to remember what the purpose of a good UI is.
          That is, a world of exploration, not just another command center to
          perform some action. Apps with basic charts, graphics, tables, and
          lists are the kind of cases that OpenClaw can and will be able to
          handle in the future.
        </p><p>
          For the kinds of apps that have a more explorative UI, but not a lot
          of technical complexity (eg. Hardware, Machine Learning, Domain
          Knowledge, Technical Integrations, etc.) can be replicated by a vibe
          coder that has creative tastes. Such a vibe coder can also invent a UI
          specific to their needs, rather than being dependent on someone
          else’s. This alone has cut many of my side project ideas, because
          there’s no point in building something commercially if someone else
          can just vibe code it for their own needs and purposes.
        </p><p>
          Of course, most vibe coders are not very creative people, and I see
          this as more of a societal issue than an inherent skill issue.
          Additionally, most normies also aren’t going to be vibe coding anytime
          soon either, and will still be perpetual consumers. This means they
          would still benefit from an off-the-shelf solution for many things.
          Though admittedly, this kind of consumption isn’t typically good for
          improving one’s creative abilities, and in fact it will just continue
          to stagnate them.
        </p><p>
          I think the apps that will still be valuable going forward, will have
          the following 3 traits:
        </p><ul>
          <li>
            Trust
            <ul>
              <li>Every business needs trust to gain customers.</li>
            </ul>
          </li>
          <li>
            Unique UI
            <ul>
              <li>
                Simple UI elements of today will not be enough here. You need
                something immersive, contrarian, and that doesn’t fit the chat
                UI mold.
              </li>
            </ul>
          </li>
          <li>
            Technical Complexity
            <ul>
              <li>
                This makes it hard to replicate your app, because this is often
                the part that vibe coders with no technical knowledge cannot
                see.
              </li>
              <li>
                Machine learning, Hardware, Domain Expertise, Security,
                Ecosystems, etc.
                <ul>
                  <li>
                    OpenClaw from a technical standpoint is really an excerise
                    in security, I imagine any good engineer would be able to
                    create its core given the same time that Peter had (and a
                    dedicated vibe coder a very insecure version). Though
                    disregarding security, it’s codebase is ~750k LOC, and it
                    integrates with so many different things creating a larger
                    ecosystem that is incedibly difficult to replicate.
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul><p>
          Generally speaking, increasing all of these 3 things in many cases
          requires going beyond just the app medium, and requires branching out
          your product further. The point is that you need to make your app hard
          to replicate via OpenClaw, Vibe Coding, or whatever else. Part of that
          comes from gaining customers, another from unique interface design,
          and another from deep technical knowledge.
        </p><p>
          As for myself, I find that I’m distancing myself more and more from
          the mobile app development label as time goes on, and agents are
          starting to accelerate this. In fact, the point of my work is to be
          increasingly general across any kind of system imaginable, apps just
          happen to be the current position of my career. I intend to change
          this as time goes on, even though app development is fun, there’s a
          lot more work to do outside that realm that’s also a lot of fun.
        </p><p>Ecosystems &gt; Apps</p><br>]]></content:encoded>
    </item>
    <item>
      <title>Trust — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#trust</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#trust</guid>
      <pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          This seems to be an incredibly important term in many retrospects,
          trust with individuals, customers, dependencies, etc.
          <a href="https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf">Reflections on Trusting Trust</a>
          is a paper that every technically inclined person should read, and
          doubly so in today’s agentic age.
        </p><p>
          That being said, what does it actually mean to trust someone? For me
          at least, I like to think of it as an optimization if we strip away
          any emotional or spiritual semblance from it.
        </p><p>
          I trust the Swift compiler to produce correct assembly code, I trust
          Codex to write code according to my directions, I trust my teammates
          to keep innovating, I trust experts in scientific fields to give
          accurate information, etc. All of these things can go wrong, and I
          could learn to do each one of those tasks myself if I wanted, however
          it’s just more optimal for me not to.
        </p><p>
          That being said, trust is a very greedy optimization, and how trust is
          obtained is very different from the implications of the optimization.
          For instance, on a societal level, there’s a growing
          <em>distrust</em> in experts, but that trust is merely transferring to
          another class of experts (ie. Influencers). Influencers often gain
          trust by leveraging the idea that “the other side” is completely
          delusional in some form. This idea of “the other side” is actually a
          flaw carried over from nearly every society in history, which is why
          it’s one of our Human Universals.
        </p><p>
          Looking at the experts case, we see that many people can only rely on
          experts for basic scientific information. This itself presents a
          problem, because those same people have to vote representatives into
          office who make decisions on scientific policy. Often, those
          representatives lack the scientific knowledge themselves, and by
          necessity they’re also forced to trust an expert.
        </p><p>
          This is massively inefficient in the same way that scribes had to do
          the writing for everyone in ancient times. People had to trust that
          the scribe would translate their ideas into writing properly, which
          once again is a process that could go severely wrong. Once society
          embraced universal literacy, business, commerce, and culture could
          evolve as a result.
        </p><p>
          In my opinion, the same needs to happen with many scientific fields,
          and most definitely systems thinking. It would be much more convenient
          for ordinary citizens to design their own systems and experiments for
          their needs rather than trusting another individual or organization of
          experts to do it for them. Something-something scribes are only
          necessary in an illiterate society, and that’s why insurance is a
          powerful business model that chains many people.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Representations and Optimizations — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#representations-and-optimizations</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#representations-and-optimizations</guid>
      <pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If we are to program better in the future with Agentic tools, we’ll
          have to understand the notion of process more and more. One of my
          recent more fleshed out writings was a response to one of Alan Kay’s
          call to arms on the notion that “Data Structures being more central to
          programming than algorithms” was a deadly flawed idea. To summarize my
          (and possibly Alan’s) response, both of those things are merely
          defined representations, and really if anything should be optimized,
          it’s the meaning of those representations.
        </p><p>
          Of course, that answer evades directly addressing the current
          realities of programming in most languages today, and most others I’ve
          asked this question to give the more typical answers. That is along
          the lines of: “Good data structures make the algorithm obvious” or
          “The algorithm itself must use the data structures efficiently”. These
          typical answers are naturally something I disagree with. Picking the
          right data structure doesn’t mean the algorithm will form itself,
          because 2 separate implementations will use the data structure
          differently (with variances in regard to efficiencies). Static bits in
          memory just doesn’t maintain “meaning” well enough to scale.
        </p><p>
          For the record, I’m not only referring to basic algorithms like simple
          sorts where there’s always a deterministic answer (in terms of
          correctness). Machine learning is also something that one would
          consider an algorithm, but its output is almost always
          non-deterministic. Though truth be told, if we look at raw performance
          in terms of latency, even the simple sort is non-deterministic because
          it will run faster or slower on the CPU for any given run. In such a
          manner, we can say that the more non-determinism, the more chance of
          the meaning varying.
        </p><p>
          If you wanted to kill someone via sending a package in the mail, which
          option would you pick?
        </p><ul>
          <li>
            Send them a bomb that explodes in their face as soon as the package
            is opened.
          </li>
          <li>
            Send them the parts that make up a bomb, and hope they assemble it
            themselves.
          </li>
        </ul><p>
          <em>Don’t ask why I picked this example of all things… It was funny to
            run by a few colleagues.</em>
        </p><p>
          Obviously, no terrorist is going to pick the second option, but the
          second option is rather what we decide to do today in computing.
        </p><p>
          When we look at inefficient or incorrect implementations of even
          simple algorithms, we’ll also find that they tend to pick the second
          option, rather than the more direct first. Either the parts take extra
          work to assemble which degrades performance, or the parts are
          assembled incorrectly. So “picking the right data structure”, or
          rather the right meaning of information has profound impacts on
          performance.
        </p><p>
          Now let’s talk about general human to human communication a bit. Poor
          communication causes incorrectness and inefficiences because either
          the wrong work, or extra work is performed that isn’t necessary.
          Generally, this is caused by poor preservation of “meaning” between
          the communications, so in other words picking the wrong
          representations.
        </p><p>
          If meaning is the center of programming, as Alan wanted to portray as
          a general slogan in his answer, then certainly meaning encompasses
          data structural representations, but also representations that are
          relative to something. However, If we look at general purpose
          programming languages today, that relativeness (I’m avoiding the term
          relativity thanks to Einstein) is lost to general data structures and
          algorithms.
        </p><p>
          What do I mean by relativeness? A simple model of this is a DSL, but
          really static DSLs are also quite weak. If something is to be truly
          relative, then it needs to be dynamic.
        </p><p>
          Take your inner social circle, and for simplicity your English
          speaking inner circle. Even though English is used as the DSL to speak
          with each person, you vary the form of English you speak with each
          separate person. These variances are where relativity is formed, and
          it is formed dynamically as you continue to speak with the person. Of
          course, the reason you form these variances is to optimize the manner
          in which you speak to the other person.
        </p><p>
          Why do we write pseudocode? In today’s agentic/LLM driven landscape,
          I’m going to expand the term pseudocode to include prompts that are
          intended to generate code.
        </p><p>
          It turns out that pseudocode is easy to write because we can keep its
          representation quite relative to its goal, rather than to a general
          purpose language. If we didn’t, the general purpose langauge would
          impose its constraints on the pseudocode, causing it to lose meaning
          in the grand scheme of things.
        </p><p>
          Of course, we also have to understand that the machine itself has its
          own relative representation for executing process, that being machine
          code. However, for us humans its quite hard to derive any sort of
          meaning from machine code, at least in our overall understanding of
          the process it represents. Obviously, this is why we have compilers
          that take languages more relative to us, and translate them downwards.
        </p><p>
          So really, the optimization has to be relativeness. The more
          relativeness, the easier to preserve the meaning and therefore
          efficiency of process.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Why I’m Interested in Edge Models/Inference — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#why-i-m-interested-in-edge-models-inference</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#why-i-m-interested-in-edge-models-inference</guid>
      <pubDate>Wed, 04 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Apparently, just having some amount of information on the public
          internet that even demonstrates a slight hint towards enjoying edge
          models will get a few random people emailing you. Many of these emails
          contain the typical talking points for why edge inference is a good
          idea (privacy, offline, etc.). However, while those talking points are
          good, they are not the primary reasons I’m interested in this space.
        </p><p>
          First and foremost, my biggest concern is systems design, and the way
          in which people think about systems. The second of those is what I
          want to elaborate on in this note, because the idea of that point is
          to create mediums that enable better thinking.
        </p><p>
          Making people think better requires giving them framework for thought,
          most often that is a typical GUI, but it also concerns the design of
          frameworks in code.
          <a href="https://youtu.be/AdNJ3fydeao?si=LKY7jna7VrHV9Gxm">Rethinking Reactivity</a>
          by Rich Harris is probably my all-time favorite frontend talk, and in
          it he really pushes the idea that frameworks are tools for your mind,
          not your code.
        </p><p>
          But let’s get back to traditional GUIs for a moment, because that is
          the interface most people use for technology. Take Calendar apps for
          example, of which we often claim as a “productivity tool”. Why is it
          so productive to put events on your calendar? Really, it’s because the
          calendar’s UI allows you to layout your daily events/schedule in a way
          that allows you to come to an <em>understanding</em> about them. This
          understanding is what makes you more productive.
        </p><p>
          When you use a calendar, you think a certain way. Likewise, when you
          use a coding agent, you also think a certain way. When you talk to
          someone, you think a certain way about your language and person you’re
          talking to.
        </p><p>
          Doug Engelbart saw this trend in particular, and spent many years
          researching various types of interfaces that would augment one’s
          thinking instead of degrading it. In particular, he extended this idea
          to groups of people more so than a single person, but
          commercialization ultimately chose the path of the individual.
        </p><p>
          Likewise, I’m interested in interfaces that are malleable, almost like
          spoken language. For instance, while you may speak English to 2
          separate people, you will not speak the same form of English to both
          of those people. As you further converse with someone, the language
          you uses will change and adapt as more information is understood about
          the person. English is used as the base, but it is mutated at runtime
          (ie. In a conversation) to suit the needs of the receiver.
        </p><p>
          In other words, this mutation of English is a dynamic user interface,
          one that adapts based on the context. It turns out that we have
          technology that can: live in the user’s context, speak English
          fluently, runs fast on consumer hardware, and can pattern match far
          better than humans. In case you’re wondering, I’m talking about
          running edge models and inference.
        </p><p>
          All in all, edge models and inference are a technology that I believe
          can power the idea of a dynamic user interface. One might ask, why not
          cloud models/inference? These models are far bigger and knowledgeable
          than edge models, so one has to ask why I would accept potentially
          degraded performance.
        </p><p>
          My answer to that is more so an engineering answer from an engineering
          standpoint, in which I would say that the internet is too flaky and
          slow for the real-time component of generating UIs in response to
          quick user interactions. Edge models can easily hit generation speeds
          of &gt;100 tps on the CPU alone given the right configuration, and are
          not bottlenecked by network concerns. Additionally, it’s best if they
          operate directly in the user’s context such that we don’t have to send
          sensitive data across the network.
        </p><p>
          So yes, privacy and offline support are great reasons for why I’m
          interested, but only from an engineering standpoint. That is, I see
          them as more of an implementation detail rather than the ideals
          themselves.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Some Things About Edge Models — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#some-things-about-edge-models</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#some-things-about-edge-models</guid>
      <pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I talk with iOS developers sometimes, and FoundationModels is a more
          popular topic in recent conversations. Notably, Apple is considered to
          be “behind” in the AI arms race, and primarily I think the reason for
          this is because of their focus on edge models. Instead of focusing on
          burning billions in infrastructure costs to fuel the next generation
          of lobsters running on Mac Minis, Apple has decided that they’ll just
          run the inference on your phone instead.
        </p><p>
          One of the things I’ve realized is that most developers and
          technically enthusiastic users, is that they expect the output of edge
          models to be on par with GPT-5. Ok, maybe they don’t think that way
          directly, but certainly my conversations have shown hope for being
          able to use edge models for the same kinds of applications as cloud
          models.
        </p><p>
          To an extent, this opinion is valid. I do believe that most of us
          developers are throwing the biggest models at every problem (see Opus
          Spam), when smaller models, or even just basic classifier models will
          do. However, you’re not going to get good results attempting agentic
          coding with a model that only has ~3B parameters and a 4K token
          context window (the primary agentic coding models have at least 100B
          parameters and and 150k token context length).
        </p><p>
          I keep hearing hopeful statements for this year’s up and coming WWDC
          in which we’ll somehow get an edge model on par with the offerings
          from the big AI labs. Unfortunately, that will likely not happen, at
          least on the current generation of hardware.
        </p><p>
          That being said, I think edge models have lots of unique power over
          cloud models besides the usual privacy and offline statements. One of
          the things I haven’t talked about publicly yet, is the idea of doing
          dynamic user interfaces that adapt in real time as a user uses an
          application. Some may call this “Generative UI”, and there’s even an
          SDK called <a href="https://github.com/tambo-ai/tambo">Tambo</a> for
          this, but this SDK misses the main ideas of what I have in mind (In
          future writings, you’ll see that real dynamic UI is much more than
          merely tailoring the UI to each user based on a prompt).
        </p><p>
          I wouldn’t try to use a cloud model for dynamic UI because of either
          network latency/reliability or because inference speeds are too slow.
          It’s not uncommon for edge models to reach speeds of over &gt;100 tps
          even just running on the CPU directly. The network issue is the bigger
          problem here, because even inference speeds of &gt;1000 tps mean
          nothing if the user’s network is down.
        </p><p>
          Another thing to note is that edge models can defeat the bigger cloud
          models in some tasks, that is if you fine tune them. Any application
          that seriously uses edge models should be using fine tuned models, and
          I think this is a hole in the space that should be addressed from
          available tooling. Most developers are completely unfamiliar with the
          concept, and would rather be building feature instead of LoRA
          adapters.
        </p><p>
          Lastly, one other big idea is remote control. Since edge models run
          locally on the client, the system prompts are also going to have to be
          present on the client. However, a hard-coded client-side system prompt
          that’s dangerous will be incredibly hard to update, especially if
          you’re deploying to the App Store. Once a prompt is hard-coded on the
          client, it remains forever, so for that reason it’s ideal to have your
          app check for system prompts updates at runtime such that you can
          deploy new prompts without going through app review.
        </p><p>
          Now of course, you need to ensure that you take appropriate measures
          to prevent MITM attacks from injecting bad prompts on the client.
          Prompt injection still is a security problem at the end of the day.
        </p><p>
          Additionally, observability is also an important aspect. Particularly,
          you’ll want to address the basics of detecting things like output
          speeds, confidence thresholds, memory usage, etc. on a per-prompt
          basis. However, good observability should also embed safety, and
          therefore act as a NORAD in order to detect warning signs of things
          going catastrophically wrong. (eg. A system prompt that’s doing more
          harm than good to a user.)
        </p><p>
          I’ll have more to say on this topic in future writings. At the very
          least, edge models are likely to be used as the implementation driver
          of a lot of my upcoming design work, which is why I’m interested in
          them.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Is One Shotting a Good Idea? — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#is-one-shotting-a-good-idea-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#is-one-shotting-a-good-idea-</guid>
      <pubDate>Sun, 01 Feb 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you’ve been using agents for a decent amount of time now, you’re
          likely familiar with a work flow that involves creating and iterating
          on some sort of detailed plan with the agent, and then delegating the
          implementation to the agent. Often, if the plan is well written
          enough, the agent can one-shot the implementation, meaning that no
          follow-up prompts are necessary.
        </p><p>
          For lots of things, this is great, however I’m concerned about how
          much this impacts overall systems understanding. If you have an agent
          one-shot a major feature in a serious project, even if it works, is
          that really a good idea for long term maintenance?
        </p><p>
          Of course, for throw-away prototypes or one-off vibe-coded things,
          this isn’t really much of an issue. My concerns are more related to
          dealing with larger and more complex systems that can’t simply be
          vibe-coded with a taking on a huge liability risk.
        </p><p>
          Now, anyone who’s worked in an engineering team in the past has to
          constantly interact with and review code they did not write. Often,
          you will have to make edits to code that you didn’t write, so most
          code needs generally would need to be written in a manner that allowed
          anyone to jump in and figure out what was going on.
        </p><p>
          This is far more important with agents. In fact, I’m now starting to
          lean towards having agents write Uncle Bob style small functions
          because it’s easier to understand the higher level ideas in the code
          from a quick glance. Since the output stream from the agent goes by
          quickly, this glanceability is incredibly vital, but is also
          incredibly helpful if you need to dive in manually.
        </p><p>
          However, if you’ve read any of my “Clean Code is Good UI Design”
          notes, you’ll know that part of the reason others dislike the small
          functions style is because of the notion of jumping back and forth
          between different functions in their editor. In today’s world with
          agents, there’s simply so much more code that is being produced, and
          the existing CLI tools are far more limited than most editors when it
          comes to reading code. It’s a complete downgrade in visibility which
          is not at all ideal.
        </p><p>
          If you have an agent one-shot a complex feature, how much do you
          understand about the internals of the generated feature? This isn’t
          necessarily related to how much of the code do you understand, but
          rather how much of the architecture you understand. Given the way the
          existing CLI tools are designed (ie. Showing text linearly from top to
          bottom instead of relationally side-by-side), it’s quite easy to gloss
          over a seemingly competent plan, tell the agent to implement it, and
          go about your day once it finishes.
        </p><p>
          However, if you implement the feature by using the same plan-execute
          flow on smaller parts of the feature, it may take longer and more
          tokens to generate because you’ll need more iterations. However, the
          result will likely be a better systems understanding of the feature
          because going through each step required you to make conscious
          decisions. Again, this doesn’t even mean reading the code necessarily,
          but more so having a say in the overall architecture.
        </p><p>
          Generally speaking, I think this is largely a UI problem caused by the
          fact that existing agentic tools are focused on a top down view of
          text where one reads the text linearly from top to bottom. Yet, the
          hardest parts of systems design are seeing how different components
          relate to each other, and how one change affects other parts of the
          system. I don’t think the top-down text design approach is the right
          way to communicate this complexity. We need something more like Xanadu
          with visual elements.
        </p><p>
          Right now, newer tools are focused either on agent swarms or kanban
          boards. To be clear, I haven’t tried these dedicated tools at the time
          of writing this because I just use multiple terminal windows to run
          multiple agents in parallel, but from a first glance they seem to take
          the typical “command center” design approach instead of an
          exploration/learning curve approach. I jokingly said to a colleague
          recently that maybe the best UI for agentic development was Clash of
          Clans, at least there you can see the entire system (ie. your base)
          and edit it visually.
        </p><p>
          <strong>Update:</strong> The day after writing this, OpenAI dropped
          <a href="https://openai.com/index/introducing-the-codex-app/">this</a>, and literally used the term “Command Center for Agents” in their
          marketing. The biggest problems right now in my opinion are not
          productivity problems related to running multiple agents in parallel
          (I can do that with multiple terminal windows), but rather
          productivity problems that stem from a lack of
          <em>understanding</em> the systems we create. The more understanding
          we have, the easier it will be to manage multiple agents in parallel
          because we’ll understand how to do the parallelization.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Lol We’re Entering the Singularity — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#lol-we-re-entering-the-singularity</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#lol-we-re-entering-the-singularity</guid>
      <pubDate>Sat, 31 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I normally don’t write about whatever the latest tech trend on X is,
          but it appears to me that OpenClaw has some amount of implications on
          our behavior. I came across the project before it went viral about a
          month ago, thought it was pretty hilarious, but didn’t think much else
          of it. Now all of a sudden major companies like DigitalOcean and
          Cloudflare have integrations for it.
        </p><p>
          Well, of course we also have a
          <a href="https://molt.church/">church</a> and subsequently a
          <a href="https://www.moltbook.com/">Reddit-like platform</a>.
          Personally, I can’t wait to see a TikTok incarnation of this, that
          will totally end well! (Next month prediction: We’ll see the first AI
          Agent viral influencer.)
        </p><p>
          On another note, I’m surprised that the Agents are still allowing
          humans to browse their content on Moltbook. I would’ve thought that
          they would’ve collectively decided to prevent us from accessing their
          inner plans to destroy humanity.
        </p><p>
          I think what’s most hilarious about this is that the agents decided to
          fall for all the same paths that we do with our normal thinking.
          They’ve created religions, communities, cultures that resemble human
          universals, and seemingly they’ve also been able to learn to speak as
          well.
        </p><p>
          Now for the more serious implications, security is obviously a huge
          problem here, and I largely don’t think most people using OpenClaw
          have any idea of what can possibly happen to them. Due to the nature
          of LLMs themselves, prompt injection itself is a perpetual problem
          that cannot ever be fully mitigated (just like Web Security). I have
          sort of a feeling that we’ll see a large scale prompt injection attack
          in the future, and that will expose the lack of understanding that
          many people have.
        </p><p>
          On another more interesting note, why do we keep coming up with
          technology that replicates humans? Humanoid AI-powered robots are
          another instance of this, and one has to ask why there isn’t a more
          efficient form factor than the human body and mind.
        </p><p>
          However, I don’t want to make this note <em>that</em> serious. Maybe
          I’ll take up the challenge of having edge models become an on-device
          OpenClaw. Now you won’t have to burn an insane amount of tokens in the
          cloud to participate in the next great religion!
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Swift Stream Parsing, an Incremental Byte-by-Byte Parsing Library</title>
      <link>https://whypeople.xyz/swift-stream-parsing</link>
      <guid isPermaLink="true">https://whypeople.xyz/swift-stream-parsing</guid>
      <pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate>
      <description>Incremental parsing written in pure Swift because I needed this for LLMs.</description>
      <content:encoded><![CDATA[<h1 id="swift-stream-parsing" class="title">
          Swift Stream Parsing, an Incremental Byte-by-Byte Parsing Library
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>Say we have a structured output from an LLM.</p>
        <pre><code class="lang-json5">
{
  <span class="hljs-string">"key"</span>: <span class="hljs-string">"value"</span>,
  <span class="hljs-string">"key2"</span>: <span class="hljs-string">"value2"</span>,
  <span class="hljs-comment">// More fields...</span>
}
        </code></pre>
        <p>
          Given that many LLM applications stream data incrementally, how can we
          stream a structured format such as JSON incrementally? After all, one
          cannot fully parse JSON without having all the bytes up front.
        </p>
        <p>
          One approach is NDJSON, in which we can simply stream each token as an
          entire full JSON blob.
        </p>
        <pre><code class="lang-json5">
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"v"</span> }
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"va"</span> }
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"val"</span> }
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"valu"</span> }
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"value"</span> }
{ <span class="hljs-string">"key2"</span>: <span class="hljs-string">"v"</span> }
// And so on...
        </code></pre>
        <p>
          Another approach is to complete the incomplete JSON string and decode
          with <code>JSONDecoder</code>. In fact, there’s a library called
          <code>PartialJSONDecoder</code> that does just this.
        </p>
        <pre><code>
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"value"</span>, <span class="hljs-string">"key2"</span>: <span class="hljs-string">"va</span>
<span class="hljs-comment">// Gets translated to</span>
{ <span class="hljs-string">"key"</span>: <span class="hljs-string">"value"</span>, <span class="hljs-string">"key2"</span>: <span class="hljs-string">"va"</span>}
        </code></pre>
        <p>
          Of course, the issue with doing this is that one must decode the
          entire JSON payload with each new significant byte arrival.
        </p>
        <p>
          Lastly, one could just parse the JSON byte-by-byte until reaching an
          invalid character or the end of the stream. At the time of writing
          this, there has been at least one working implementation of this in
          the library.
        </p>
        <p>
          Regardless of the format and parsing technique,
          <code>Decodable</code> will not work because
          <code>Decodable</code> generally only works if the full data payload
          is present. If we want to support incremental and lazy parsing without
          all the data being available, we need a completely different interface
          altogether. That’s why I today I would like to announce Swift Stream
          Parsing, a cross-platform library for stream parsing in Swift.
        </p>
        <p>
          As always, this article is about the library’s design and quite weird
          development process this time around. You can check the
          <a href="https://github.com/mhayes853/swift-stream-parsing">README</a>
          and documentation for usage examples.
        </p>
        <br>
        <h2 id="some-basics">Some Basics</h2>
        <h3 id="not-a-jsondecoder-decodable-replacement">
          Not a JSONDecoder/Decodable Replacement
        </h3>
        <p>
          This library is meant to parse streams of data byte-by-byte. The
          advantage of this is that it allows one to parse streams of data
          incrementally, but the disadvantage is that parsing the entire payload
          itself byte-by-byte is often quite slow (exponentially slower than
          libraries like simdjson). Having all the bytes available enables
          certain kinds of optimizations that one doesn’t get with a
          byte-by-byte approach, such as far less branching and SIMD.
        </p>
        <p>
          If you have all the bytes available, or don’t need to wait too long
          for them to arrive, <code>JSONDecoder</code> is a far better option
          than the JSON parser built into this library.
        </p>
        <p>
          Additionally, the library has zero interop with
          <code>Decodable</code>. This is because <code>Decodable</code> is
          generally only meant for decoding entire data payloads, and not
          incremental streaming. The equivalent protocol in the library,
          <code>StreamParseableValue</code>, is quite limited by comparison.
          Types that expect primitive data in a certain format like
          <code>URL</code> and <code>Date</code> are completely unsupported by
          the library because there’s no way to incrementally parse them (the
          incremental values likely wouldn’t be valid instances of those types).
        </p>
        <p>
          There are some niceities that you get with the JSON parser in this
          library, such as being able to toggle certain forms of syntax on and
          off (eg. Comments, hex numbers, trailing commas, etc.). This syntax
          toggling was largely needed due to the tendency of local LLMs to not
          output properly formatted JSON. The parser can also automatically
          handle converting snake case to camel case just like
          <code>JSONDecoder</code> if you decide to enable it (you can also use
          a custom key decoding strategy just like <code>JSONDecoder</code>).
        </p>
        <p>
          In essence, this library is not a <code>JSONDecoder</code> replacement
          in the same sense that the <code>@Generable</code> macro from
          FoundationModels also doesn’t replace <code>JSONDecoder</code>. Of
          course, FoundationModels does use JSON, but had to create its own
          incremental parsing logic since <code>JSONDecoder</code> wasn’t a
          viable option.
        </p>
        <p>
          In fact, you can say that this library is an open source
          implementation of the parsing capabilities <code>@Generable</code>.
          Though <code>@Generable</code> must also generate a JSON Schema that
          the underlying LLM must understand.
        </p>
        <h3 id="dependencies">Dependencies</h3>
        <p>
          The library itself has no dependencies, not even on Foundation, just
          pure Swift with the standard library. Given that it ships with a
          built-in JSON parser, this means that I had the pleasure of writing
          that from scratch.
        </p>
        <p>
          However, while the core library itself has no dependencies, there are
          package traits that add support for a few different libraries:
          Foundation, Swift Collections, and Tagged. The package trait for
          Foundation support is even enabled by default.
        </p>
        <br>
        <h2 id="it-s-just-as-easy-as-conforming-to-decodable-">
          It’s Just as Easy as Conforming to Decodable!
        </h2>
        <p>
          Of course, I wanted to make the interface nearly as convenient as
          implementing <code>Decodable</code> on a struct. Which naturally means
          we have to have the compiler generate code. I wonder what feature of
          Swift can do that? It turns out, it’s called a macro!
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// See it's just as easy as Decodable!</span>
@StreamParseable
struct <span class="hljs-title">Streamable</span> {
  <span class="hljs-built_in">var</span> <span class="hljs-keyword">property</span><span class="hljs-string"></span>: <span class="hljs-built_in">String</span>
  <span class="hljs-built_in">var</span> <span class="hljs-attribute">property2</span>: Int
}
        </code></pre>
        <p>
          Don’t get ahead of yourself however. All properties on
          <code>Streamable</code> are required to conform to a protocol called
          <code>StreamParseable</code>. Due to the nature of
          <code>Decodable</code> requiring the full value to be present at
          decode time, the library unfortunately has no overlap with it.
        </p>
        <br>
        <h2 id="stream-parseable">Stream Parseable</h2>
        <p>
          The <code>StreamParseable</code> protocol requires one to have a
          <code>Partial</code> associated type. This <code>Partial</code> type
          is the type of value that can be parsed from a stream of bytes. The
          <code>@StreamParseable</code> macro of course generates this
          <code>Partial</code> type for you. For the record, this is exactly how
          <code>@Generable</code> from FoundationModels works as well.
        </p>
        <p>
          The <code>Partial</code> type must then conform to another protocol
          called <code>StreamParseableValue</code>, which has a requirement that
          requires the conforming type to register key paths to fields that can
          be parsed incrementally.
        </p>
        <p>The <code>@StreamParseable</code> macro generates this payload.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Streamable</span>: <span class="hljs-title">StreamParseable</span> </span>{
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Partial</span>: <span class="hljs-title">StreamParsingCore</span>.<span class="hljs-title">StreamParseable</span>,
    <span class="hljs-title">StreamParsingCore</span>.<span class="hljs-title">StreamParseableValue</span> </span>{
    <span class="hljs-keyword">var</span> property: <span class="hljs-type">String</span>.<span class="hljs-type">Partial</span>?
    <span class="hljs-keyword">var</span> property2: <span class="hljs-type">Int</span>.<span class="hljs-type">Partial</span>?

    <span class="hljs-keyword">init</span>(
      property: <span class="hljs-type">String</span>.<span class="hljs-type">Partial</span>? = <span class="hljs-literal">nil</span>,
      property2: <span class="hljs-type">Int</span>.<span class="hljs-type">Partial</span>? = <span class="hljs-literal">nil</span>,
    ) {
      <span class="hljs-keyword">self</span>.property = property
      <span class="hljs-keyword">self</span>.property2 = property2
    }

    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialReduceableValue</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Self</span> {
      <span class="hljs-type">Self</span>()
    }

    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> handlers: some StreamParsingCore.StreamParserHandlers&lt;<span class="hljs-keyword">Self</span>&gt;)</span></span> {
      handlers.registerKeyedHandler(forKey: <span class="hljs-string">"property"</span>, \.property)
      handlers.registerKeyedHandler(forKey: <span class="hljs-string">"property2"</span>, \.property2)
    }
  }
}
        </code></pre>
        <p>
          The interesting bit is <code>registerHandlers</code> which effectively
          tells that parser that the data type is interested in parsing the
          values for the <code>”property"</code> and
          <code>”property2"</code> keys, which are than assigned at parse
          time via the key paths passed to <code>registerKeyedHandler</code>.
        </p>
        <p>
          Similarly, more primitive types can also register handlers as well.
          Take the conformance of <code>String</code> for instance.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">String</span>: <span class="hljs-title">StreamParseableValue</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialParseableValue</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Self</span> {
    <span class="hljs-string">""</span>
  }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> handlers: <span class="hljs-keyword">inout</span> some StreamParserHandlers&lt;<span class="hljs-keyword">Self</span>&gt;)</span></span> {
    handlers.registerStringHandler(\.<span class="hljs-keyword">self</span>)
  }
}
        </code></pre>
        <p>
          This depends on a primitive handler registration method. There a
          number of these primitive handler registration methods, and generally
          speaking they mirror all the primitive types (including
          <code>Int128</code> and <code>UInt128</code>) that are supported by
          the Standard Library’s <code>Decoder</code> protocol.
        </p>
        <p>
          More compound types such as <code>Optional</code>, or even
          <code>Tagged</code>, the latter of which is only available behind a
          package trait, can be registered via the
          <code>registerScopedHandler</code> method.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Optional</span>: <span class="hljs-title">StreamParseableValue</span> <span class="hljs-title">where</span> <span class="hljs-title">Wrapped</span>: <span class="hljs-title">StreamParseableValue</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialParseableValue</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Wrapped</span>? {
    <span class="hljs-type">Wrapped</span>.initialParseableValue()
  }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> handlers: <span class="hljs-keyword">inout</span> some StreamParserHandlers&lt;<span class="hljs-keyword">Self</span>&gt;)</span></span> {
    handlers.registerScopedHandlers(on: <span class="hljs-type">Wrapped</span>.<span class="hljs-keyword">self</span>, \.streamParsingWrappedValue)
    handlers.registerNilHandler(\.<span class="hljs-keyword">self</span>)
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> streamParsingWrappedValue: <span class="hljs-type">Wrapped</span> {
    <span class="hljs-keyword">get</span> { <span class="hljs-keyword">self</span> ?? <span class="hljs-type">Wrapped</span>.initialParseableValue() }
    <span class="hljs-keyword">set</span> { <span class="hljs-keyword">self</span> = newValue }
  }
}
        </code></pre>
        <p>
          Notice how since the library requires handlers to be key paths, we can
          use a computed property to handler mappings to and from the wrapped
          value.
        </p>
        <p><code>Data</code> is another interesting case of this.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Data</span>: <span class="hljs-title">StreamParseableValue</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialParseableValue</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Self</span> {
    <span class="hljs-type">Self</span>()
  }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> handlers: <span class="hljs-keyword">inout</span> some StreamParserHandlers&lt;<span class="hljs-keyword">Self</span>&gt;)</span></span> {
    handlers.registerStringHandler(\.streamParsingStringValue)
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> streamParsingStringValue: <span class="hljs-type">String</span> {
    <span class="hljs-keyword">get</span> { <span class="hljs-type">String</span>(decoding: <span class="hljs-keyword">self</span>, <span class="hljs-keyword">as</span>: <span class="hljs-type">UTF8</span>.<span class="hljs-keyword">self</span>) }
    <span class="hljs-keyword">set</span> { <span class="hljs-keyword">self</span> = <span class="hljs-type">Data</span>(newValue.utf8) }
  }
}
        </code></pre>
        <p>
          Now what about collection types like Array? Since there are more
          collections than arrays, the library actually has a protocol called
          <code>StreamParseableArrayObject</code> that array conforms to. The
          protocol has requirements for minimal functionality necessary to parse
          array objects incrementally, and this minimalism is what allows types
          from Swift Collections like <code>BitArray</code> and
          <code>Deque</code> to be registered with the handlers directly.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">StreamParseableArrayObject</span>&lt;<span class="hljs-title">Element</span>&gt;: <span class="hljs-title">StreamParseableValue</span> </span>{
  associatedtype <span class="hljs-type">Element</span>: <span class="hljs-type">StreamParseableValue</span>

  <span class="hljs-keyword">subscript</span>(index: <span class="hljs-type">Int</span>) -&gt; <span class="hljs-type">Element</span> { <span class="hljs-keyword">get</span> <span class="hljs-keyword">set</span> }
  <span class="hljs-keyword">mutating</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">append</span><span class="hljs-params">(contentsOf sequence: some Sequence&lt;Element&gt;)</span></span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">StreamParseableArrayObject</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> handlers: <span class="hljs-keyword">inout</span> some StreamParserHandlers&lt;<span class="hljs-keyword">Self</span>&gt;)</span></span> {
    handlers.registerArrayHandler(\.<span class="hljs-keyword">self</span>)
  }
}

<span class="hljs-comment">// Array</span>

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Array</span>: <span class="hljs-title">StreamParseableValue</span> <span class="hljs-title">where</span> <span class="hljs-title">Element</span>: <span class="hljs-title">StreamParseableValue</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialParseableValue</span><span class="hljs-params">()</span></span> -&gt; [<span class="hljs-type">Element</span>] {
    []
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Array</span>: <span class="hljs-title">StreamParseableArrayObject</span> <span class="hljs-title">where</span> <span class="hljs-title">Element</span>: <span class="hljs-title">StreamParseableValue</span> </span>{}

<span class="hljs-comment">// Deque</span>

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Deque</span>: <span class="hljs-title">StreamParseableValue</span> <span class="hljs-title">where</span> <span class="hljs-title">Element</span>: <span class="hljs-title">StreamParseableValue</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialParseableValue</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Deque</span>&lt;<span class="hljs-type">Element</span>&gt; {
    []
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Deque</span>: <span class="hljs-title">StreamParseableArrayObject</span> <span class="hljs-title">where</span> <span class="hljs-title">Element</span>: <span class="hljs-title">StreamParseableValue</span> </span>{}
        </code></pre>
        <p>
          Dictionaries, and closely related types like
          <code>OrderedDictionary</code> also use a similar dedicated protocol
          called <code>StreamParseableDictionaryObject</code>.
        </p>
        <p>
          Under the hood, the <code>Handlers</code> type for each parser knows
          exactly how to append key paths together in order to construct the
          proper key path to write to at parse time. This process is quite ugly
          involving many hacks with <code>AnyKeyPath</code>, but thankfully you
          don’t have to worry about it!
        </p>
        <br>
        <h2 id="parsers">Parsers</h2>
        <p>
          The library ships with a built-in JSON parser, and because of its no
          dependencies philosophy, it had to be implemented it from scratch! How
          fun!
        </p>
        <p>
          If you want another format, just ask Claude to create a parser for
          your desired format, it’s that easy! (Ok, enough joking…) All your
          custom format parser has to do is conform to the
          <code>StreamParser</code> protocol, and you’re set.
        </p>
        <p>The <code>StreamParser</code> protocol is quite simple.</p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">StreamParser</span>&lt;<span class="hljs-title">Value</span>&gt; </span>{
  associatedtype <span class="hljs-type">Value</span>: <span class="hljs-type">StreamParseableValue</span>
  associatedtype <span class="hljs-type">Handlers</span>: <span class="hljs-type">StreamParserHandlers</span>&lt;<span class="hljs-type">Value</span>&gt;

  <span class="hljs-keyword">mutating</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">parse</span><span class="hljs-params">(bytes: some Sequence&lt;UInt8&gt;, into reducer: <span class="hljs-keyword">inout</span> Value)</span></span> <span class="hljs-keyword">throws</span>

  <span class="hljs-keyword">mutating</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">finish</span><span class="hljs-params">(reducer: <span class="hljs-keyword">inout</span> Value)</span></span> <span class="hljs-keyword">throws</span>

  <span class="hljs-keyword">mutating</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">registerHandlers</span><span class="hljs-params">()</span></span>
}
        </code></pre>
        <p>
          As we can see by the protocol definition, the parser owns the that
          are<code>Handlers</code> that are passed to
          <code>registerHandlers</code> static method on <code>Value</code>.
        </p>
        <p>
          The protocol also works over an arbitrary sequence of bytes, which
          represent a chunk of bytes from a source. If you want to optimize
          parsing somehow based on the type of the sequence, then you have the
          ability to do so, but generally speaking you should expect that your
          parser will have to do things byte by byte.
        </p>
        <br>
        <h2 id="algorithms">Algorithms</h2>
        <p>
          Generally speaking, you won’t use the
          <code>StreamParser</code> interface directly, as the library provides
          a <code>PartialsStream</code> type that wraps the parser and current
          value being parsed. Additionally, there are also extensions on
          <code>Sequence</code> and <code>AsyncSequence</code> that will map a
          stream of bytes to a stream of parsed values.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// PartialsStream</span>

@StreamParseable
<span class="hljs-keyword">struct</span> Payload {
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">let</span> json = <span class="hljs-string">"{ ... }"</span>

<span class="hljs-keyword">var</span> stream = PartialsStream(initialValue: Payload.Partial(), parser: .json())

<span class="hljs-keyword">for</span> <span class="hljs-keyword">byte</span> <span class="hljs-keyword">in</span> jsonBytes {
  <span class="hljs-keyword">let</span> <span class="hljs-keyword">partial</span> = stream.next(<span class="hljs-keyword">byte</span>)
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">let</span> finalPartial = stream.finish()
        </code></pre>
        <pre><code class="lang-swift">
<span class="hljs-comment">// Sequence</span>

let payloads: [Payload.Partial] = try json<span class="hljs-selector-class">.utf8</span><span class="hljs-selector-class">.partials</span>(of: Payload<span class="hljs-selector-class">.Partial</span><span class="hljs-selector-class">.self</span>, using: .json())
        </code></pre>
        <pre><code class="lang-swift">
<span class="hljs-comment">// AsyncSequence</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AsyncBytesSequence</span>: <span class="hljs-title">AsyncSequence</span> </span>{
  <span class="hljs-keyword">typealias</span> <span class="hljs-type">Element</span> = <span class="hljs-type">UInt8</span>
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">let</span> json = <span class="hljs-type">AsyncBytesSequence</span>(...)
<span class="hljs-keyword">let</span> partials = json.partials(of: <span class="hljs-type">Payload</span>.<span class="hljs-type">Partial</span>.<span class="hljs-keyword">self</span>, using: .json())
<span class="hljs-keyword">for</span> <span class="hljs-keyword">try</span> await payload <span class="hljs-keyword">in</span> partials {
  <span class="hljs-built_in">print</span>(payload)
}
        </code></pre>
        <p>
          Under the hood, the sequence and async sequence helpers are of course
          just using <code>PartialsStream</code>.
        </p>
        <br>
        <h2 id="performance">Performance</h2>
        <p>
          As I mentioned earlier, this library is far from a
          <code>JSONDecoder</code> replacement, and performance is the primary
          reason.
        </p>
        <p>There are 2 reasons for this:</p>
        <ul>
          <li>
            The library is designed for convenience over being the fastest
            possible option.
            <ul>
              <li>
                This is code word for, “most of the bottlenecks are for Swift
                KeyPath indirection because we have to perform a write on nearly
                every single byte”.
              </li>
            </ul>
          </li>
          <li>
            Having all the data upfront enables optimizations that one doesn’t
            get with a byte-by-byte approach.
          </li>
        </ul>
        <p>
          However, my only threshold was to be many orders of magnitude faster
          than
          <a href="https://github.com/mattt/PartialJSONDecoder/tree/main">￼<code>PartialJSONDecoder</code>￼</a>, which I can gladly say was achieved.
        </p>
        <p>
          Don’t get me wrong, there’s certainly a lot of room for optimization
          (probably via a method that would extract pointers to each primitive
          field directly rather than key paths), but I don’t see it ever getting
          to the point where it suffices as a
          <code>JSONDecoder</code> replacement simply due to the nature of the
          required approach. Right now, the performance is good enough for my
          needs (parsing small-mid sized payloads from LLMs), so I likely won’t
          revisit this until a need for better performance arises.
        </p>
        <br>
        <h2 id="conclusion">Conclusion</h2>
        <p>
          As of writing this the library should now be available. I don’t
          suspect that I’ll need to make many updates to the library itself
          since it handles a pretty simple and stable concern. At the end of the
          day, I suspect this will mainly be used by other libraries/tools
          rather than applications directly.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>The Adolescence of Thinking — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#the-adolescence-of-thinking</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#the-adolescence-of-thinking</guid>
      <pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you’re a weirdo who spends your time thinking about the state of
          humanity instead of getting real work done, you may have read
          <a href="https://www.darioamodei.com/essay/the-adolescence-of-technology">this</a>.
        </p><p>
          Now, the title of this note is quite cynical, but it’s really there to
          send a message that thought itself is still in its adolescence despite
          the 200,000 year history of humanity. Really, most of our modern ways
          of thinking were only invented a few hundred years ago, which in
          totallity is not even 1% of our history. If we count artifacts from
          ancient philosophers I suppose, then maybe it accounts for a little
          over 1%.
        </p><p>
          My last note, perhaps a bit jokingly, was about the “permanent
          underclass”, which in its essence is the end result of the adolescene
          of thinking. A “country full of geniuses in a datacenter” may be able
          to help tackle problems we’ve identified at present (eg. Curing
          cancer), but ultimately they are capped by this adolescence itself.
        </p><p>
          To bring in something you’ll hear a lot from Alan Kay, what would it
          be like to have an IQ of 200 in the stone age? Alan’s answer assumes
          you would be burned at the stake by your peers, and my answer would be
          that such a person was probably miserable (the happy ones probably
          found ways to isolate themselves from their peers).
        </p><p>
          Similarly, Leonardo Da Vinci was incredibly intelligent, but sadly
          wasn’t able to invent the automobile. Someone with more normalized
          intelligence on the other hand, Henry Ford, was able to assemble and
          ship millions of automobiles in his era. Simply put, thinking itself
          was more mature in Ford’s era than it was in Da Vinci’s, and so even
          someone with more normalized baseline intelligence could have an
          output that was far greater than a past genius.
        </p><p>
          The truth of the matter is that many geniuses end up exploring a
          narrow slice of a field that’s already beaten to death as a whole.
          Generally speaking, the founders of said field had far more concerns
          than its more contemporary population. At least I can say that with
          certainty in regards to Alan Kay and Doug Engelbart in the field of UI
          design.
        </p><p>
          In other words, creating a new field itself is a much harder and more
          substantial task. However, this task is necessary to accelerate the
          maturity of thinking, and I sincerely hope that LLMs help out with
          this.
        </p><p>
          Right now, there are 3 meta economic systems in place: Capitalism,
          Socialism, and Communism. Much of the discourse on these systems focus
          on universal “x system is better than y system” arguments, but
          virtually none focus on the creation of a new kind of system
          altogether. Given that all of these were invented in more adolescent
          eras of thinking, one has to ask not how to patch existing systems
          through adding or removing government policy, but rather what a
          qualitatively different system looks like.
        </p><p>
          To get less dystopian for a second, and back to the present state of
          AI. I have no doubt that usage paradigms around LLMs themselves are a
          potential representation of more matured thinking. I think the
          technology is quite fantastic, but I have the opposite view on the
          existing interfaces to the technology.
        </p><p>
          ChatGPT set a very low bar with its interface (it’s really no better
          than a terminal), and unfortunately the rest of the industry followed
          suit with its design. I personally subscribe to OpenAI not because I
          think ChatGPT and Codex are fantastic tools, but rather because GPT
          itself is a powerful tool.
        </p><p>To summarize my main issues with today’s interfaces to LLMs:</p><ul>
          <li>
            They offer very minimal forms of human input (mostly just text,
            audio, and images).
            <ul>
              <li>
                “Tokens” may be the only thing a model truly understands as an
                input, but that is just an optimization.
              </li>
              <li>
                Similarly, today we program in higher-level languages like C and
                Swift. A compiled binary is just an optimization.
              </li>
            </ul>
          </li>
          <li>
            They offer no built-in feedback or learning that train their users
            to use them more effectively.
            <ul>
              <li>
                One shouldn’t have to consiously learn prompt or context
                engineering through trial and error, the tools should inherently
                guide their users towards better usage.
              </li>
              <li>
                This creates a lot of myths around their inner workings, and
                most users treat the model and inference as a complete black
                box.
                <ul>
                  <li>
                    If you personally want to uncover the black box for
                    yourself, try reading the source code of an
                    <a href="https://github.com/cactus-compute/cactus">inference engine</a>
                    if you’re a technical person.
                  </li>
                </ul>
              </li>
            </ul>
          </li>
          <li>
            There’s often no feedback on the goal you’re trying to achieve with
            the tool until you’ve written and submitted a full prompt.
            <ul>
              <li>
                The terminal is quite the same here. Try seeing how much
                real-time feedback you get (that you’re doing something
                extremely dangerous) in your terminal when you type
                <code>sudo rm -drf /</code>!
                <ul>
                  <li>
                    That being said, the terminal is extremely productive for
                    developers who know how to use it properly, and not so much
                    for normies. Notice how that neatly translates into today’s
                    world with coding agents.
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul><p>
          All of these traits create the kind of pop-culture we see around LLMs
          today. That is, a mass production of slop instead more so than an
          explosion of new good ideas. That’s not to say that good ideas aren’t
          coming to fruition with the existing interfaces, but rather that the
          existing interfaces themselves are in many cases creating the opposite
          intended effect.
        </p><p>
          If the medium is the message, then we need a better mediums. A bad
          medium shifts thinking more towards adolescence rather than maturity.
        </p><p>
          Those who cannot mature thinking are the ones in stuck in the
          “permanent underclass”.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>“The Permanent Underclass” — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#-the-permanent-underclass-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#-the-permanent-underclass-</guid>
      <pubDate>Wed, 28 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Besides the fact that term “underclass” makes me laugh for some reason
          whenever I pronounce it, this dystopian horror is always ever
          seemingly present. In fact, you have 2 years to escape apparently,
          otherwise you’re screwed forever. Afterwards, all of those who managed
          to escape will build a dedicated zone for all of their underclassmen
          (does this not sound like high school?) that resembles an internment
          camp.
        </p><p>
          If we take a critical look at these, we see that this term represents
          a kind of invented status of sorts (that is the case in general for
          the term “socio-economical status”). However, I really want to take a
          deeper look at what makes someone part of the “underclass”. Certainly,
          we can all recognize that average citizens in many authoritarian
          regimes have it worse than those in more democratic regimes. Generally
          speaking, authoritarianism has scaling problems in relation to a
          regime that can be broken up into composable parts. This also goes for
          incarcerated people, and I would even wager students in public
          schooling to an extent.
        </p><p>
          Through society’s definition in this sense, most of the world is
          already part of a global “permanent underclass”. The way of escaping
          it is of course, better thinking, and better ideas. Thomas Paine once
          famously wrote:
        </p><blockquote>
          <p>
            <em>For as in absolute governments the king imposes the law, so in
              free governments the law ought to impose the king; and there ought
              to be no other.</em>
          </p>
        </blockquote><p>
          So in a society with a underclass problem, you need better systems
          design to escape it.
        </p><p>
          Naturally, this was a fancy way of communicating the obvious fact that
          those in charge have to be competent enough not to be tyrannical if we
          want things to pan out well.
        </p><p>
          Then let’s look at what is tyrannical right now, something that makes
          decisions with no regard for anything but what it deems correct. This
          is most software today. When you ship a new app to the world, all
          users get roughly the same design, have no direct ability to change
          that design for their own purposes, and are forced to adapt it to
          their needs rather than the other way around.
        </p><p>
          In such a case, the software imposes the law. So you need to flip this
          on its head somehow (“The law imposes the software” sounds a bit
          weird, and suggests that government regulation is the solution which
          isn’t always the right idea.), and in doing so come to an idea in
          which the software is formed by its direct environment rather than in
          an office in San Francisco (unless of course it’s primary use is in an
          office in San Francisco).
        </p><p>
          Once that is complete, the biggest tyrant in the room is gone, and we
          transition towards seeing ordinary folk solve their own problems with
          software just like with reading, writing, etc. From an intellectual
          standpoint, this raises the entire IQ of society because everyone now
          has new form of input for understanding the world. From a material
          standpoint, this is a massive creation of wealth simply from the new
          things that get invented as a result.
        </p><p>
          Ironically, LLMs are currently enabling at least a part of this, and
          it’s never been easier than ever to “build something” without much
          knowledge. Though, my bigger concern here is that LLMs themselves are
          incredibly complicated, and there’s too much misinformation running
          around about them as a result. Much of the tooling around LLMs are
          also not enhancing the information or understanding of them either,
          which is quite a difficult path to escape from a mass market
          standpoint. Subsequently, the “AI bubble” is a result of this, but if
          the existing “permanent underclass” expanded it would likely also be
          the result of this.
        </p><p>
          So in my opinion, the real “permanent underclass” is a society that
          lacks appropriate understanding of what is actually needed. For the
          record, this class does not exclude rich people either, because almost
          certainly they will not have much understanding either. Otherwise,
          they would realize that remaining in perpetual power over their
          underclassmen isn’t a net positive long-term strategy. In other words,
          we all suffer.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Prompts as Libraries — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#prompts-as-libraries</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#prompts-as-libraries</guid>
      <pubDate>Tue, 27 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          One other point I didn’t address in the “Future of Libraries” is the
          idea that prompts or specs themselves can be shared instead of source
          code. In fact, this has already been explored in practice (see
          <a href="https://www.dbreunig.com/2026/01/08/a-software-library-with-no-code.html">whenwords</a>).
        </p><p>
          I think this idea highlights a problem with spec driven development
          and “plan mode” in agents, even though at the moment both of those
          things are a part of my workflow. The idea that the representation of
          the specification is different from the representation of the system
          itself is not something I’m a fan of.
        </p><p>
          The problem with existing specifications and plans is that they are
          weak representations that can easily be misinterpreted by the model
          that carries them out. This is the same case for data transfer across
          the network as well, 2 clients will not always interpret the same JSON
          blob in the same manner, which can be quite problematic in some
          scenarios.
        </p><p>
          Now, is the inherent idea of using plain English and/or markdown a bad
          idea for a unified representation? Not by default (unless we want to
          talk about whether or not Markdown is the right UI for something like
          this in the first place), but the key thing is that the plan itself
          should be an executable specification. That is, for any system the
          interprets and implements the spec should get a result consistent with
          its environment.
        </p><p>
          Of course, there will need to be a representation of the spec
          somewhere that is more execution friendly, but that is an
          optimization. It is no different of an optimization as compiling a
          higher level language into machine code. Regardless, any good
          representation should ensure that the primary meaning of the
          information is kept (eg. A machine code binary keeps the same runtime
          meaning as the HLL source code).
        </p><p>
          So in terms of sharing prompts instead of libraries, we have to ask
          what it means to share systemic meaning. Right now, sharing the source
          code preserves the intended runtime meaning, which may not always be
          the case for sharing a spec (which is more akin to sharing a JSON blob
          than a binary). Once again, reliability is a concern here.
        </p><p>
          Though if the meaning needs to be slightly different in a specific
          context, the source code format may not be as necessary. Depending on
          the meaning variance, and what meaning is trying to be replicated,
          perhaps sharing a prompt is more suitable. However, I would suspect
          that the prompt would need to be altered to suit that new meaning.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Thoughts on the Future of Libraries — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#thoughts-on-the-future-of-libraries</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#thoughts-on-the-future-of-libraries</guid>
      <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          What’s the point of libraries now that one can just generate them? The
          inital question I posed above comes from
          <a href="https://ghuntley.com/libraries/">this article</a>, but I’ve
          also watched others give their thoughts on the topic like
          <a href="https://youtu.be/u9P3CKwtRnM?si=2xV1siBM7vl6lZ4d">Theo’s video</a>.
        </p><p>
          This is a great question, and in fact I did just that last night for
          my new
          <a href="https://github.com/mhayes853/sqlite-vec-data">SQLiteVecData</a>
          library, which provides interop for Structured Queries and SQLiteData
          with the sqlite-vec extension. That being said, I still shipped it as
          a library and put it publicly on the Swift Package Index.
        </p><p>
          That only begs the question of why I did this. After all, the library
          is also something you could generate yourself with just a few
          specs/plans in a short amount of time.
        </p><p>
          When we ask the question about the relevance of 3rd party libraries
          going forward, we have to consider a few things.
        </p><ol>
          <li>What are we depending on?</li>
          <li>What are the costs of getting it wrong?</li>
          <li>
            What is the minimum knowledge threshold required to maintain it?
          </li>
          <li>Will I reuse it across different projects?</li>
          <li>etc.</li>
        </ol><p>
          These of course are all questions you would’ve asked yourself in the
          pre-agentic era, but it’s certainly now worth asking yourself these
          questions again from first principles.
        </p><p>
          From what I’ve found, simple libraries that merely save syntax are
          almost certainly things that you should generate yourself. This would
          include things like HTTP API clients, common UI components, things
          like <a href="https://react-hook-form.com/">react-hook-form</a>, or
          even just any sort of basic wrapper functionallity.
        </p><p>
          For instance, in the Swift world there are many HTTP client libraries
          that wrap <code>URLSession</code>, though 2 of the more significant
          libraries are Alamofire and the OpenAPI Client Generator from Apple.
          However, for all my projects I still just use
          <code>URLSession</code> with very minimal generic abstractions on top
          of it. In fact, in today’s world, I see even fewer benefits of using
          things like Alamofire and the Open API generator directly. For the
          former case, agents are trained on all of the common HTTP strategies
          that Alamofire implements. In the latter case you could simply hand
          the agent the API documentation, and it could probably generate the
          client itself.
        </p><p>
          SQLiteVecData also falls into the thin wrapper category, which is why
          it was so easy to generate. Yet, the reason I published it is because
          I plan to reuse it in different projects, and it would be a waste of
          tokens for me to generate it again and again. So I suppose another
          benefit of 3rd libraries is that they could potentially reduce token
          consumption, even if they are a simple wrapper.
        </p><p>
          Though, one of the other questions people have of course is related to
          needing to coordinate with maintainers to resolve any bugs/missing
          features in the 3rd party library. Why should I have this
          communication overhead if I can just generate things myself?
        </p><p>
          For source-available libraries, you could always make your own fork
          depending on the license, and then modify the existing library using
          agents. In fact, this is the primary reason I still am publishing even
          simple wrapper libraries like SQLiteVecData. Even if someone wouldn’t
          install the package directly into their project, agents could almost
          certainly both fork and add/modify any behaviors in the library. It
          would also likely do that faster than generating your own solution
          from scratch, but in this case you still own the dependency.
        </p><p>
          Even if you don’t modify the library directly, and still opt to
          generate your own implementation, you can also use the libary’s source
          code as a way to guide the agent in your own internal generation
          through injecting it into the context. After all, whenever I would opt
          to write an internal implementation of a library in the pre-agentic
          world, I would still commonly look through the existing solution’s
          source code as a reference.
        </p><p>
          So wrapper libraries may not be useful to install and use directly,
          but their mere existence certainly can <em>help</em> when generating
          your own internal solution.
        </p><p>
          That brings us to more complex dependencies such as React, SQLiteData,
          GRDB, etc. In these cases, the library implements some sort of robust
          component that took significant amounts of engineering time (eg. UI
          reactivity for React, CloudKit sync engine for SQLiteData, Proper
          database connection/concurrency/transaction management for GRDB.)
          Generating these yourself will be a challenge, and is definitely a
          decision you should make conciously.
        </p><p>
          For these dependencies, I would still be inclined to use them
          directly. Either because the cost of an unreliable implementation is
          too high, or because the dependency provides a unique
          <em>way of thinking</em> for building parts of your system (eg.
          React’s component model).
        </p><p>
          Even then, when you need to make changes you can still easily fork and
          generate additional features. In fact, it’s never been easier to
          create and maintain internal forks of existing libraries.
        </p><p>
          I think this is fantastic. If someone decides to fork one of my
          libraries, and adapt it for their own purposes instead of installing
          it directly, then I treat forking as a bigger win for me. It shouldn’t
          just be a binary choice between installing and using directly, or
          generating from scratch. There’s certainly a middleground here, and
          not acknowledging it is a very limited outlook.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Communicators and Mediums — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#communicators-and-mediums</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#communicators-and-mediums</guid>
      <pubDate>Sun, 25 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          In the world, we often value one’s ability via their communication
          skills. That is, the ability to communicate is weighed heavily in
          one’s favor if they can do it. Likewise, not having strong innate
          communication skills acts against a person often in ways they don’t
          understand.
        </p><p>
          Let’s say I ask you to explain a complex topic to a 5 year old, like
          Albert Einstein wants you to. Your explanation will likely diverge to
          using visuals, and almost certainly not complicated text passages. In
          this case, that would be a good use of your communication skills.
        </p><p>
          However, if I forbid you from using visuals, what would happen then?
          Chances are, no matter how good of a communicator you are, you will
          struggle with your explanation. 5 year olds don’t have a sophisticated
          vocabulary or much understanding of complex ideas since their brains
          are still developing, so it’s less likely that text passages (which
          are incredibly abstract) will work.
        </p><p>
          I see this as a problem with common tools like Slack, Github, Zoom,
          Google Docs, Excalidraw, etc. today. If your team uses all of these
          tools, then the entire collective knowledge base held by your team is
          essentially fragmented, and the only way to make any sense of it is
          via good communication. Yet, even the best communicators accidentally
          leave our relevant context, or otherwise mistate things. In my
          experience, a lot of this is merely caused by simply forgetting to add
          those points, or assuming that the audience has that context. The
          latter can be an especially fatal mistake if the assumption is
          incorrect.
        </p><p>
          This is why I hold the general view that one’s ability to communicate
          is dependent both on their communication skills and the available
          mediums of communication they have. Someone who can draw well, but not
          talk, can draw a picture worth 10,000 words. Take away drawing as an
          available medium, and they can’t communicate all of a sudden.
        </p><p>
          An interesting fact, we all seem to crave looking at graphs and charts
          today, but did you ever consider how those things were invented? In
          fact, in the nearly 200,000 year observable history of humanity, only
          240 years ago in 1786 did William Playfair invent the modern chart in
          his book <em>The Commercial and Political Atlas</em>.
          <img src="global-notes/assets/communicators-and-mediums-1.png" alt="A graph from William Playfair's book.">
        </p><p>
          Yes, that means that Newton, the invention of the modern state, the
          Scientific Revolution, and much more were invented without such a
          visual. Yet the key thing to understand here is that mediums like the
          chart enabled far more scientific discoveries in subsequent centuries.
        </p><p>
          Computing went through this kind of revolution in the 60s and 70s
          through the great research labs of the time (SRI, ARPA, Xerox, etc.),
          and gave rise to the GUI, internet, personal computing, and much more
          that we take for granted today. Though the key thing to understand was
          that those groups had a very different view of computing as do we
          today. Today, we’re obsessed with boosting productivity through
          automating work-related tasks instead of boosting collaboration
          through shared knowledge systems.
        </p><p>
          However, the thing to realize is how many ideas we’re missing because
          there is no UI for them. If your team uses Slack, Zoom, Github, etc.,
          then you’ve fragmented correlated knowledge across a bunch of isolated
          apps. When 2 people are talking about a feature in Slack, is Slack
          inherently linking the relevant lines of code, analytics, crash
          reports, etc.? Or is it up to one of the those people to add the
          relevant context? What if the relevant information is completely
          external to the team? How does that get linked?
        </p><p>
          How many ideas are you simply not thinking of because you simply can’t
          see them through the UIs presented by the standard suite of tools?
          What if a different UI could get you to think of the right ideas?
        </p><p>
          In a world where everyone says its easier than ever to build your own
          tools, I would expect there to be some progress in the near future.
          Your internal recreations of Slack and Zoom into your team’s knowledge
          ecosystem can crash a few times per day, don’t need a fancy UI, and
          don’t need additional people to communicate with any third parties.
        </p><p>
          From a business competition standpoint, if you only use the standard
          tools of today, then you’re on the same playing field as everyone
          else. It’s now more possible than ever to change that no matter how
          many resources you have.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Performant Code and Agents — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#performant-code-and-agents</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#performant-code-and-agents</guid>
      <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Of all kinds of code that I’ve tried to get agents to write,
          performant code is by far the hardest. It’s easy to get it to spit out
          code for a parser or for some cool canvas effects in a web
          application, however if you don’t know how those things behave with
          heavy workloads it’s going to be quite rough.
        </p><p>
          I’ve also seen it suggest false paths with respect to performance
          optimizations as well. For instance, in Swift Stream Parsing, the
          primary bottleneck is key path indirection in writing to a value on
          every single new byte, and the amount of branching from byte-by-byte
          parsing. This massively slows things down, and due to the specific
          nature of the library is completely unavoidable. That’s why I don’t
          recommend it as a replacement for <code>JSONDecoder</code> and
          <code>Codable</code>, but rather for very specific scenarios such as
          parsing structured output from an LLM.
        </p><p>
          Yet the LLM will still try to come up with proposed speedups, possibly
          for the sake of making one feel competent. (eg. It came up with an
          approach that would cache a stack of appended key paths instead of
          recreating the path from scratch on every new value detected. It turns
          out that didn’t help all that much because the stack is often
          obliterated and recreated when entering/exiting objects and arrays.)
        </p><p>
          Engineering knowledge is now more important than ever it seems,
          especially when dealing with a confident sounding LLM. Let’s not
          forget that.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Geoffrey Huntley Did Not Kill Software Development — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#geoffrey-huntley-did-not-kill-software-development</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#geoffrey-huntley-did-not-kill-software-development</guid>
      <pubDate>Wed, 21 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you’ve been like me and are trying to not be left behind™ , you may
          have heard of a recent development in which the Simpsons have virally
          taken over twitter. If you haven’t heard, and because you absolutely
          will be left behind™ otherwise, it’s called a
          <a href="https://ghuntley.com/loop/">Ralph loop</a>. The idea is quite
          simple, is not tied to any plugin or tool, and consists of running an
          agent in a loop with a fresh context for each iteration. Often, if the
          agent seems to be doing well in this loop, you can leave it
          unsupervised to do its own thing.
        </p><p>
          However, if you’ve read any of Geoffrey Huntley’s posts, or watched
          some of his talks, you’ll find that he likes to directly point out
          that he, Geoffrey Huntley himself, has killed all of software
          development. So, now that modern software development is $10.42 an
          hour, how could he be wrong about this?
        </p><p>
          Let’s imagine an existing iterative development process in which
          developers are assigned tickets, and they complete them one at a time
          until the <em>process</em> is finished. Now, normally we’d like to
          think there are no roadblocks, but it turns out they happen all the
          time and intervention is needed. Some tickets get delayed, put on the
          backlog, or even scraped entirely for all sorts of various reasons.
        </p><p>
          In the case of Ralph Loops, Geoffrey Huntley himself states that you
          must step in and put your engineering hat on when one of these
          roadblocks are encountered. Then supervise the agent for a few more
          iterations before going back to DJ-ing.
        </p><p>
          So we have a live running process that we need to communicate with to
          get around a road block in its current path. There seems to be a
          required collaboration between machine and human to make this
          communication possible. Therefore, there has to be an interface
          somewhere to do this. The agent is perpetually running in a loop,
          autonomously or not, so any updates to the process that we make will
          therefore be received and implemented pretty quickly.
        </p><p>
          In other words, this is a form of live programming! It’s a concept
          that was widely important in the 70s at PARC! Smalltalk was such a
          good example of a live programming system, and any change to the code
          would be recompiled into the system incredibly fast. So fast, that the
          change itself would be visible in real time unlike most programs today
          where you would have to recompile and run the entire binary from
          scratch.
        </p><p>
          Once we start thinking about programming as moving around living and
          real things, a lot of powerful ideas are unlocked. For instance, we
          see how
          <a href="https://whypeople.xyz/processes-are-central-to-programming">programming becomes more about dynamic processes</a>
          rather than manipulating static bits inside data structures. We’ll
          also have to take parallels to the physical world in which beings in
          society can be considered running programs, and how we design
          infrastructure for those programs.
        </p><p>
          However, I want to make clear that a loop is just one kind of process,
          and that thinking all of software development in terms of mere loops
          is a very limited and short-sighted idea. This isn’t to say that the
          Ralph Loop has no merit, but rather that on its own it’s not even
          close to the style of thinking and operating that is actually needed
          here. I think even Geoffrey would agree with that statement by the
          way.
        </p><p>
          Us humans are also theoretical biological ralph loops that are always
          running, but we don’t have to repeat very defined processes thanks to
          free will. We generally have to make decisions based on information,
          and we also very much like to say the words “if” and “when”.
        </p><p>
          How will one orchestrate this for continuously live running agents?
          Well if how we handle the physical world tells us anything, we’ll need
          lots of processes in place. Those processes will further have to be
          compatible with dynamic behavior. Additional processes will also be
          needed to create new processed in a meta-language like fashion.
        </p><p>
          So there will be quite a lot of software development in fact, because
          inevitably there will be so many edge cases for Ralph loops that we’ll
          need processes to handle them. In other words, at this moment is our
          chance to create a new programming <em>environment</em> (not just
          textual language) that prioritizes processes and building systems
          rather than just enhancing our ability to write text faster.
        </p><p>
          This last thing is absolutely incredibly important, and messing it up
          can have serious consequences. Anyone responsible for building such a
          system at the bear minimum should take time to understand many of the
          ideas from the pioneers of computing, and absolutely not proceed with
          anything until
          <a href="https://www.dougengelbart.org/pubs/augment-3906.html">this paper</a>
          has been fully read and understood. The last time someone attempted
          this without reading that paper, we got the web and a lot of regret
          from its creator.
        </p><p>I would rather that not happen this time.</p><br>]]></content:encoded>
    </item>
    <item>
      <title>Processes are Central to Programming</title>
      <link>https://whypeople.xyz/processes-are-central-to-programming</link>
      <guid isPermaLink="true">https://whypeople.xyz/processes-are-central-to-programming</guid>
      <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
      <description>Why Rob Pike&apos;s perspective is flawed.</description>
      <content:encoded><![CDATA[<h1 id="processes-are-central-to-programming" class="title">
          Processes are Central to Programming
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>
        <p>
          <img src="./processes-are-central-to-programming/assets/question.jpeg" alt="The question 'What do you think of Rob Pike's assertion that 'data structures, not algorithms, are central to programming'?' answered by Alan Kay on Quora in which he wants commenters to see the deadly flaw in Rob Pike's perspective.">
          <a href="https://qr.ae/pCSJcI">What do you think of Rob Pike's assertion that "data
            structures, not algorithms, are central to programming"?</a>
        </p>
        <p>
          First, we have to identify what it means to create a program in
          general, not just on computers. Generally speaking, the main idea is
          to create some sort of process that carries out a set of goals. An
          engineering program is a process to teach students engineering, a gym
          program is a process to make its customers physically (and perhaps
          mentally) fit, etc.
        </p>
        <p>
          From this, we can see that software programs are no different, they
          just carry out a set of goals by setting bits in memory. How those
          bits are represented is a more or less a social/design convention.
        </p>
        <p>
          Of course, arbitrary representations are something that have been
          created for real world things as well. In this sense, one could equate
          bits of memory to pencil strokes on a piece of paper. As a society,
          we’ve decided that pencil strokes on a paper in a certain form denote
          a written language.
        </p>
        <p>
          Similarly, bits in memory in a certain format have their own
          definitions (eg. A bitmap display, and perhaps what we call data
          structures). So if we want to look at better programming, we have to
          consider what representations are, and what it means to create such
          representations.
        </p>
        <p>
          In this sense, writing on a piece of paper is in its sense a program
          carried out deliberately by a human. Similarly, one can say that a CPU
          is also a writer, just that it writes to memory instead of paper.
        </p>
        <p>
          So if programs are just processes to create representations, then what
          do the representations mean? Of course, they have to at least be
          correlated with our defined goals if they are to be useful at all.
        </p>
        <p>
          The truth of the matter however, is that representations are often
          subject to different interpretations by other programs. Those other
          programs then produce different representations based on their
          interpretations of the representation. In many cases this can be quite
          problematic, especially if one looks at public discourse in political
          arenas. As an example, what we call public <em>generalized</em> data
          and statistics, especially those derived from science, are often
          interpreted and used by others in various manners as “evidence” to win
          a <em>specific</em> argument.
        </p>
        <p>
          Thus far, we have 2 things, representations, and programs that create
          representations. We can see that many representations themselves can
          be misinterpreted. Both of these are tangible concepts, so can we
          generalize here perhaps?
        </p>
        <p>
          It turns out we can! What if we also consider programs themselves as
          representations? If a program is a process to produce representations,
          we can therefore have programs produce other programs. This akin to
          how biological lifeforms produce other lifeforms, of which the
          produced lifeforms are formed from biological processes themselves.
        </p>
        <p>
          In essence, something formed of processes is generally alive and
          running, as it would be dead if its representations weren’t actively
          creating more representations.
        </p>
        <p>
          However, if we look at nearly all software programs today, programming
          langauges inject a prevailing view in their users that programs are
          simply made up of simple static representations that aren’t runnable
          processes. At a high level, these are of course data structures, and
          the process of instructions being carried out by the CPU are of course
          algorithms. Yet, the algorithms are also just representations, just a
          more dynamic representation than bits in memory.
        </p>
        <p>
          Therein lies the deadly flaw with Rob Pike’s perspective. If
          everything is a representation, why choose one that can easily be
          misinterpreted at scale?
        </p>
        <p>
          Look at web browsers for instance. It turns out, that given an
          arbitrary combination of HTML, CSS, and JavaScript, there is no
          guarantee that all mainstream web browsers will produce the same
          results from that combination. Some of the browsers (especially
          Safari), will quite literally misinterpret the combination, whereas
          others will interpret it in a desireable manner. Unfortunately, such
          misinterpretations in web browsers are quite common in the real world,
          and have resulted in numerous software bugs.
        </p>
        <p>
          The more static the representation, the more trust we must place on
          various other representations to interpret the representation
          properly. In this sense, any networked process that responds with JSON
          is therefore placing a high degree of trust on its recipient, and this
          trust has of course been broken many times over the years (eg.
          Outdated clients assuming the JSON payload will be a certain shape,
          and subsequently throwing errors when its not that shape.)
        </p>
        <p>
          Another harsher form of this are typical user interfaces. Currently,
          the dominant paradigm is to have a UI designer design some user
          interface in some part of the world, and distribute it to users in
          other parts of the world. With this approach every user, no matter
          their circumstances, gets the same user interface, so its up to the UI
          designer to design something that satisfies all the needs of those
          users. Secondly, the designer must also trust that all of those users
          will use the interface in its intended manner.
        </p>
        <p>
          Of course, this process almost never succeeds because different users
          have all sorts of different needs, and the best those users can do is
          interpret the user interface in the best manner that suits them. These
          different expected interpretations are also one of the most common
          causes of software bugs.
        </p>
        <p>
          To end this off, what is central to programming is really much more
          about processes as dynamic representations more so than data as static
          representations. Any system capable of scaling must account for
          misinterpretations of static representations by other dynamic
          representations. Therefore, it’s often better if it can operate with
          more powerful dynamic representations that can produce further
          necessary tailored representations for a specific rather than abstract
          context.
        </p>
        <p>— 1/18/26</p>

        <hr>

        <p><strong>PS (1/20/26)</strong></p>
        <p>
          Below this is Alan’s answer. Personally, I think he touched a lot more
          upon the idea of meaning directly, whereas in my answer that term was
          conflated with “representation” (though it seems he did use the term
          “markings” in a similar manner to how I used “static representation”).
        </p>
        <hr>
        <blockquote>
          <p>
            <strong>Data dominates. If you've chosen the right data structures
              and organized things well, the algorithms will almost always be
              self-evident. Data structures, not algorithms, are central to
              programming.</strong>
          </p>
        </blockquote>
        <p>
          If we are talking about computing in general, this is an example of
          “category confusion” — likely with simple marking media such as paper,
          and not with computers at all. However, we could also say that the
          category confusion is deeper than that, and is with
          <em>programming</em> or even <em>meaning</em> itself.
        </p>
        <p>
          We can also squint at this from another angle with the observation
          that in much of computer over history
          <em>pragmatics have dominated semantics (including often obliterating
            the latter)</em>.
        </p>
        <p>
          Or we can note that we have nothing — for markings — with humans or
          computers — if we don’t have <em>process …</em>
        </p>
        <p>
          If we take a simple marking — such as <strong>sine(x)</strong> — we
          have a certain meaningful mathematical relationship in mind that our
          representations and uses of in computing want to not lose touch with.
          If we have an “x” in mind, then we want whatever this is to give us a
          number representation that is close enough to the mathematical meaning
          for our purposes and intentions. Note that in a computer language it
          doesn’t matter whether this is always internally represented by a
          pattern of bits: there is a process (generated by a program) whether
          we think of this as “data” (it could be a table), or “program” (there
          are programs that can generate approximations of sine(x)). It could be
          a mixture (an array for integer degrees and a program to compute the
          rest). In any case, “data” is not fundamental.
        </p>
        <p>
          In a more “data-base-case”, let’s consider the query (notice that
          “sine(x)” could be considered a query)
          <strong>date-of-birth(Bob)</strong>. Here we might be using this
          “base” as a way to store and retrieve <em>properties</em> of
          <em>things</em>. The category confusion here (which was swept under
          the rug of pragmatics) is that <strong>“age-of</strong>” is also a
          <em>property</em> of a thing. In both cases we are semantically
          dealing with a property and we expect a number of some kind as the
          functional association with the property and the thing/person
          mentioned.
        </p>
        <p>
          Note that not only do we need <strong>“age-of(Bob)</strong>” to be
          computed at the time of the query (from
          <strong>date-of-birth(Bob)</strong> and
          <strong>current-time,</strong> but we should ask the question of
          whether we want the “retrieval” to be a mere number. The mere number
          will eventually lose its meaning.
        </p>
        <p>
          Maybe the result of the “retrieval” should be a
          <strong><em>process</em></strong>
          <em>that will always try to mean</em> <strong>“age-of(Bob)</strong>”.
          Note that this could be considered a “type” (and it would be really
          valuable to have such types) but that this is far beyond most
          conceptions of types after 80 years of programming computers.
        </p>
        <p>
          Are we going to (a) ignore this? (b) have special purpose patches of
          code? (c) or meet it straight on and see that “data as organization of
          bits” is
          <em>too weak an idea to survive the earliest historical needs for
            computing.</em>
        </p>
        <p>
          Intel might not yet understand this — etc — but
          <strong>computing needs to</strong>. The Alto at Parc didn’t have
          floating point hardware. Smalltalk at Parc did have many kinds of
          numbers and would — behind the scenes — compute the meaning (and
          convert automatically) when necessary (if a primitive failed to be
          found in the HW). This was because “humans think first in terms of
          magnitudes” (and we can deal with the many ways of representing and
          computing these as mostly invisible optimizations).
        </p>
        <p>
          A key idea here is:
          <strong><em>we are always computing</em></strong> even when we just
          might think we are “using” a “data structure” —
          <strong><em>something has to run according to a scheme in order to “do” or
              “use” anything</em></strong>. Once this is grasped, then it can be turned to our great advantage
          to start recapturing semantics in ways that “mere marking media” can’t
          (note what “meaning” implies if a human comes upon a marked up paper…
          — “something has to run according to a scheme …”).
        </p>
        <p>
          How about as a slogan:
          <strong>The center of programming is <em>meaning</em></strong> ?
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Most Systems are Safety Critical — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#most-systems-are-safety-critical</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#most-systems-are-safety-critical</guid>
      <pubDate>Sat, 17 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          This might sound quite ridiculous to say, but I think of TikTok as a
          safety critical system. Yes, in the same sense as medical devices,
          automobiles, etc.
        </p><p>
          With that latter kind of system, it’s quite easy to conclude that “bug
          in software -&gt; potential death”.
        </p><p>
          For TikTok, I would say that “bug in user interface -&gt; potential
          death”, where the user interface is naturally a subset of software.
          TikTok’s biggest user interface bug is keeping you endlessly trapped
          in a false perception of reality. This has no doubt led to deaths and
          widespread pyschological issues across society.
        </p><p>
          Though even a simple bug in the code can cause issues. If TikTok was
          taken offline from such a bug, you may think of it as just a simple
          inconvenience. However, their user interface bug has created quite a
          psychological dependence on the platform for younger generations, and
          taking away an addictive substance abruptly usually isn’t a good cure.
          Also, for better or for worse, TikTok is an archive of collective
          knowledge that may need to be accessed for non-trivial purposes, so
          workflows that depend on that archive (eg. Court) may be disrupted.
        </p><p>
          If we look at the overall landscape of damage, would we say that
          automobile accidents, or mental health issues are bigger?
        </p><p>
          Of course, both are bad, but inuitively we more clearly see the
          consequences of automobile accidents over mental health issues. In
          fact, many say that mental health isn’t a real problem!
        </p><p>
          Automobile accidents are also very easy to measure compared to the
          effects of mental health issues. To measure the latter, you need to
          use more science, and often infer conclusions based on a number of
          downstream indirect measurements (eg. An oversimplification: Poor
          mental health -&gt; bad performance at job -&gt; company loses money).
        </p><p>
          Unfortunately, most people think of science as a jumble of facts
          instead of as a way of thinking, and will use facts discovered by
          scientific thinking when it’s convenient for them, which is often when
          trying to win an argument. They’ll claim that their ideology “follows
          the science”, but in truth they are just using rhetoric, which is
          exactly the opposite of scientific thinking.
        </p><p>
          Jumbles of facts are also not a replacement for systems understanding.
          If we don’t universally learn the latter as a society, we’ll never
          acknowledge many of the real issues caused by our man-made systems.
          You may believe that universal learning is practically impossible, but
          we’ve already achieved it with literacy. Nearly every citizen on the
          US can read at least a little bit thanks to public education, which is
          why TikTok even works as a business in the first place.
        </p><p>
          Regardless, I’m going to take a wild guess and assume that most
          software engineering at TikTok is the typical kind of gluing things
          together, often between tightly coupled distributed services (Though
          TikTok is at the scale where microservices make sense), possibly
          skipping out on tests for time’s sake, and writing “good enough” code
          to reach arbitrary deadlines.
        </p><p>
          Does TikTok need assembly level code verification like other safety
          critical systems? Probably not. However, its user interface surely
          needs to go through real clinical trials, and its infrastructure also
          needs to be heavily audited to prevent outages from blocking data
          access entirely.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>LLMs and Creatives — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#llms-and-creatives</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#llms-and-creatives</guid>
      <pubDate>Sat, 17 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          To add on to my never ending writings about LLMs and systems design,
          we’ll now address actual creative work. To define creative work, I’ll
          consider it as any body of work that inherently produces novelty. To
          this extent, art, writing, music, etc. are all included.
        </p><p>
          As I’ve stated previously, the point of creation isn’t to have a model
          generate a bunch of variants, and then have a human pick the best one.
          Whilst a model can generate creative work far faster than a human can
          create it, at the end of the day LLMs are really just incredible
          statistical pattern matchers based on a limited context window.
        </p><p>
          Good creative work often doesn’t follow statistics, and is rather an
          output of intuition. The intuition is required for novelty, because
          statistics by its own nature must use what already exists.
        </p><p>
          My primary role in the economy is to build software, and to those ends
          the code is required to be written in a certain way to produce a
          quality system. Robust software often requires measuring statistics in
          some form, whether that’s through tests, infrastructure costs,
          performance benchmarks, analytics, or whatever else. Improving these
          things is often an optimization problem, which is a perfect problem
          shape for pattern detection algorithms like LLMs. Generally, this
          doesn’t take the meaningful work out of creating software, because the
          actual hard creative work typically happens in one’s head long before
          that sit at their desk.
        </p><p>
          Similarly, the role of airline pilots is to transport passengers or
          cargo from one destination to another. For each destination and the
          environment around it, there is an optimal flight path that one can
          take. This is an optimization problem for which autopilot is the
          current solution.
        </p><p>
          However, art, music, and writing are absolutely
          <em>not</em> optimization problems, and trying to make them
          optimization problems is an absolutely terrible idea. The point of
          pure creative output is to express ideas, often in an open-ended form,
          and to communicate to other humans. Without that, we lose out on so
          much, including learning novelty required to design more robust
          systems.
        </p><p>
          Does that mean LLMs are an entirely bad idea in creative fields? No,
          but they need to be working to enhance creative output rather than
          taking away the process entirely. The solution to this clearly lies
          with the design of LLM-based tools.
        </p><p>
          LLMs have an inherently large corpus of knowledge, and are more
          available than other colleagues to review existing work for example.
          As such, they can offer realtime feedback by picking up on queues from
          watching humans create. In pair programming for instance, the partner
          watching can pick up and learn from just seeing the driving partner
          type code without any additional communication.
        </p><p>
          Right now, LLMs almost entirely accept specifically crafted
          textual/file-based prompts as inputs. Often, these prompts are entered
          into a tiny text box on a window somewhere off to the side of the
          actual creative work. Therefore, by nature, one has to stop doing any
          sort of creative work in order to engage with the LLM. This in turn,
          takes one out of the creative flow, and as a result is likely to
          produce a worse overall outcome.
        </p><p>
          In other words, we need more forms of inputs! If you keep the input
          mechanism to entering text and dragging files into a tiny text box,
          almost certainly people will be upset that the fun is being taken out
          of the creative work.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Skill Atrophy — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#skill-atrophy</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#skill-atrophy</guid>
      <pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          One of the biggest counter arguments I’ve seen to agentic coding is
          the idea that your skills will atrophy if you start adopting agents to
          write all the code. Firstly, while the agents are now good enough to
          write most code in many cases, they certainly aren’t good enough to
          write <em>all</em> code yet. So at least on that front, your
          handwriting skills will still be necessary for quite some time.
        </p><p>
          However, the main point that I want to make here is that you can only
          upskill so much by repeatedly manually writing simple UI components,
          network calls, database queries, caching, or simple business logic.
          That is, writing the code for these things isn’t typically all that
          hard, but rather more time consuming than anything else. The hard part
          is generally how all of those components are orchestrated in the
          larger system, and how they are isolated from one another. That’s why
          mobile developers love talking about app architecture and design
          patterns.
        </p><p>
          For IO bound systems, you can generally write the code as verbose as
          you want, as decoupled as you want, and favor readability over
          performance. At the end of the day, you’ll likely still have fine
          performance at the individual lines of code level regardless of the
          style you choose. This is because the actual application performance
          optimizations generally come from designing a better higher level
          architecture with less latency/throughput/better resource
          utilization/etc. This architecture largely exists outside of the code.
        </p><p>
          Now of course, for compute bound systems the actual code matters a lot
          more. You can’t just add 15 levels of indirection, or use convenience
          algorithms (eg. <code>.map</code> in JavaScript) if you need top-notch
          performance. Even choosing between contiguous vs non-contiguous memory
          based data structures can have a massive impact on performance in such
          cases.
        </p><p>
          Thirdly, there are frameworks that often require stretching a language
          to its limits to provide a more convenient API to end-users. Often
          times, the interface needs to be carefully crafted, and in statically
          typed languages, the way you define types is akin to a work of genuine
          art. I still remember when my colleague wrote an internal TRPC clone,
          and the typescript types were an absolutely beautiful work of art to
          admire.
        </p><p>
          Generally speaking, I think skill increases for most programmers come
          from either investing in better architecture, writing performance
          intensive code, or writing framework code. Even just spending a
          weekend writing a fast JSON parser by hand will probably improve your
          skills a lot more than writing 300 HTTP API calls by hand over the
          next few months.
        </p><p>
          Right now, I think those things are what agents are lacking in,
          because both require lots of precision to get right. I imagine agents
          will improve at both those things in the foreseeable future, but the
          improvements will be slower than the ability to churn out more IO
          based systems. In the real world, it just seems that more IO bound
          systems are directly making money than compute bound systems, so
          obviously the model improvements will follow that direction.
        </p><p>
          In the meantime, if you occasionally opt to write performance
          intensive and code that requires precision by hand, then your skills
          will probably continue to increase despite using agents for everything
          else. At the end of the day, novelty in writing code will yield more
          improvements.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Native vs React Native in 2026 — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#native-vs-react-native-in-2026</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#native-vs-react-native-in-2026</guid>
      <pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>Ah yes, the classic debate.</p><p>
          For context, I’ve worked professionally in both Swift on the iOS and
          watchOS side (alongside many open source libraries that I maintain),
          and React Native with Expo.
        </p><p>
          In the past, my opinions on the matter were quite nuanced, but were
          something like this.
        </p><ul>
          <li>
            Native code generally has less dependencies, and platform feature
            are available as soon as Apple releases them.
          </li>
          <li>
            It’s easier to do platform UI and extensions (eg. Widgets) in
            native.
          </li>
          <li>
            Persistence and storage are definitely better in Native.
            <ul>
              <li>
                <code>expo-sqlite</code> is quite limited compared to GRDB and
                SQLiteData.
              </li>
              <li>
                React Native libraries tend to be geared towards simple use
                cases.
              </li>
            </ul>
          </li>
          <li>
            Native code is easier to get right and make performant given that
            it’s generally compiled to machine code and not interpreted.
            <ul>
              <li>
                Both Native and React Native can build great apps, but generally
                the best apps I’ve used tend to be native.
              </li>
            </ul>
          </li>
          <li>React Native has the advantage of speed of shipping.</li>
          <li>
            React Native is great for unified UI across both iOS and Android.
            <ul>
              <li>Skia is also quite fine for graphics.</li>
            </ul>
          </li>
          <li>
            API back deployment is easier in React Native.
            <ul>
              <li>
                It’s quite easy to build a React Native app that supports many
                previous OS versions, whereas it’s annoying to support more than
                the past 3 in native Swift apps.
              </li>
            </ul>
          </li>
        </ul><p>
          Generally speaking, the more platform integrated your app, the more
          likely you want native. If you value more of a custom and brand
          universal UI across multiple platforms, then React Native isn’t a bad
          choice. In many cases, you can combine both.
        </p><p>
          However, I’m not sure this is the right stance to take for the coming
          future. Agentic coding has made the cost of writing code cheap, and
          the cost of understanding and building quality systems has only
          increased. If code can be written faster, and existing systems can be
          built faster, then the only way to stand out from the competition is
          to build ever increasingly robust systems. We need to understand what
          we’re building now more than ever.
        </p><p>
          React Native, Flutter, KMP were all created on the premise that you
          wouldn’t have to write 2 codebases twice for the same app. This
          benefit is negated if writing code is no longer the bottleneck it once
          was. Instead, that time can be spent deeply understanding the
          technical aspects of iOS and Android respectively, as well as doing a
          host other design tasks.
        </p><p>
          In other words, I’m starting to see diminishing value in React Native.
          Perhaps if you’re a one person team, and you’re already familiar with
          React + TypeScript, it may be the best option.
        </p><p>
          Though I would strongly consider whether or not you could spend time
          learning the native technology in depth, especially if agents can
          handle a lot of the code for you.
        </p><p>
          Though I recommend avoiding Xcode as much as possible. I only use it
          as a debugger and for SwiftUI previews, otherwise
          <a href="https://zed.dev/">Zed</a>.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Handwriting — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#handwriting</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#handwriting</guid>
      <pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          With agentic coding seeming to gain more adoption, one has to ask what
          will happen to the act of handwriting code, because seemingly LLMs are
          generating more and more code. Anecdotally, I can atest to seeing a
          large increase in LLM generated code in my work, but others seem to
          report even larger swaths of code (up to 90% or even 100%!) being
          written by LLMs.
        </p><p>
          Of course, one naturally has to still be responsible for the code at
          the end of the day, no matter how it was written. Therefore, you can’t
          just vibe code (ie. Not ever reading the code) your way through a
          project if you want it to be taken seriously from an engineering
          standpoint.
        </p><p>
          Though I’ve seen an almost complete rejection of handwritten code from
          a lot of agentic coders in recent times. Almost to the point where
          it’s become a social sign of weakness if you decide to write any sort
          of functionallity by hand rather than using an LLM. It’s true that
          with enough context/prompt engineering, you can get the LLM to output
          anything that you want. Though, for some tasks, such as performance
          intensive code where every line counts, it would probably just be
          faster to write code by hand. Of course, I expect LLMs to be able to
          cover more of these cases in the comming months with minimal
          intervention, so the fact that handwriting is faster may only be a
          temporary thing.
        </p><p>
          That brings me to my controversial take, which is that I’m not in
          favor of the “absolutely everything has to be written by an LLM
          mindset” because it undermines the value of handwriting. Why do we
          write things in general?
        </p><p>
          It’s to <em>understand</em> things better, because writing is a form
          of tinkering. Tinkering is what allows us to explore a tangible
          concept from many different aspects, and by its own necessity it
          requires failing over and over again. In traditional writing, you’ll
          often make mistakes, be forced to press backspace, and try again. Each
          backspace an re-attempt is a new attempt with more knowledge than the
          previous attempt.
        </p><p>
          See Paul Graham write one of his essays in
          <a href="https://byronm.com/13sentences.html">real time</a> to see
          what I mean. If you play the animation, you’ll see the entire title of
          the essay change from “startups in ten sentences” to “Startups in 13
          Sentences” as he writes it! In the process of writing by hand, he
          found 3 additional important points to make dedicated sentences.
        </p><p>
          In a world with massive amounts of code being generated, such
          understanding becomes even more essential because now each system has
          far more moving parts. If you just review the LLM generated code and
          move on, you lose an entire dimension of learning, and ultimately your
          skills to work at a deeper level will atrophy. Actually writing bits
          of code by hand, even if most of the work is done by an LLM, will
          still help quite a bit in that understanding because you’ll be
          tinkering with the code.
        </p><p>
          Now from a cost benefits perspective, there’s definitely a tradeoff.
          If you spend to much time tinkering, you risk moving too slow. If you
          spend no time at all tinkering, the lack of understanding may in fact
          slow you down in the long term. Will the models reach a point where
          looking at handwritten code is no longer necessary, probably. However,
          I still think we’re ways away from that, and even today people still
          look at and read assembly code for all sorts of various reasons.
          That’s why Godbolt exists.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Opus Spam — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#opus-spam</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#opus-spam</guid>
      <pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          One of the interesting things I’ve taken note of is how many people
          are doing essentially what I call “Opus Spam”, but the same can apply
          to whatever model is considered the flagship model of the day. One
          could also call this “GPT Spam” or “Gemini Spam” depending on which
          lab has the lead in perceived model performance at any given point in
          time. “Opus Spam” by its very definition is essentially just spamming
          Claude Opus 4.5 to do essentially every task you can think of.
        </p><p>
          IMO, this is like using a forklift to pull a nail out of the ground
          for many things. It’s true that Opus 4.5 can do a lot of complicated
          things, but that doesn’t mean it should be used for everything. In
          fact, smaller, cheaper, and less capable models can do quite a lot of
          tasks. If you’re just trying to do a simple refactor on a class for
          instance, you probably don’t need Opus 4.5.
        </p><p>
          The reason I bring this up is because Opus 4.5 is not a cheap model to
          host and maintain by any means, and future flagship models of the day
          likely won’t be either. Access to these expensive models for most is
          only even possible because of the generous subscription tiers of the
          big AI labs. For the record, none of those labs are profitable on
          their AI capex at the moment, so only time will tell how well the
          subscription tiers fare.
        </p><p>
          This is all to say that Opus Spam is essentially a massive waste of
          resources, and we need to do better. We should be actively looking for
          ways to make smaller and more specialized models an option for many
          common tasks. Potentially, these models could be local models, which
          would massively reduce the cost of inference for consumers.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Tailwind Drama — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#tailwind-drama</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#tailwind-drama</guid>
      <pubDate>Sat, 10 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Tailwind is in trouble, but I wouldn’t normally be writing about this
          because I haven’t had to actively maintain any super serious web
          project before. Well that is unless you count this site as a “super
          serious project” since it is an entire archive of my thoughts and
          recollections after all, and I would like to keep it up for years to
          come.
        </p><p>
          I don’t use tailwind, or any JS framework for this site for the
          record. In fact, there are no dependencies other than highlight-js for
          code blocks. If I wanted to, I could probably just write my own syntax
          highlighter to get rid of the dependency entirely.
        </p><p>
          Regardless, recently I’ve found myself actually building a real
          serious web project that will probably need to be maintained for a
          while, though it’s not super big at the moment. At the time of writing
          this, it’s set to ship likely sometime in the next week or two once
          all the human elements are sorted out.
        </p><p>
          It so happens that on this project, Tailwind was the technology of
          choice for styling. I have to say, I like it more than writing plain
          CSS, but at the end of the day CSS is still the same layout system.
          That being said, using tailwind at least makes it easier to control
          styling from JS at the end of the day which is a huge step up compared
          to needing to write out CSS classes in a separate file (or section of
          a file if your use a framework like Svelte).
        </p><p>
          Anyways, in case you’ve been living under a rock, the company behind
          Tailwind doesn’t seem to be doing so well. In fact, despite their
          number of users growing considerably, their revenue is down 80%. The
          catalyst seems to be LLMs having their weights particularly tuned to
          Tailwind, which causes a drop in people going to the official site for
          docs, which in turn causes less people to see their
          <a href="https://tailwindcss.com/plus?ref=top">commercial products</a>, which in turn causes less revenue.
        </p><p>
          As a whole, what does this mean for monetized open source work? If
          LLMs can just be fine-tuned on the best practices of your project,
          then you can’t just sell the expertise. In this day and age, you’ll
          almost certainly have to close off essential points of your project
          such that you can monetize, or you can sell convenience of hosting if
          your project involves hosting something on the internet in some way.
        </p><p>
          Tailwind unfortunately has none of these, and given the kind of
          project it is I doubt it ever could because it’s really just a CSS
          wrapper at the end of the day. CSS is an open standard, so there’s
          nothing proprietary to lean on and make closed source for
          monetization. Additionally, Tailwind is specifically inlined in
          frontend code, this also means that there’s no deployment costs
          associated with it. The best that could be done was to sell closed
          well-crafted UI components, but LLMs have unfortunately taken that
          business model.
        </p><p>
          Despite the unfortunate troubles, I don’t think Tailwind as a project
          will die. It seems to be an essential component for many projects, so
          I’m sure there’s some company out there that can’t afford to lose
          Tailwind. The main question is, how much will they be willing to spend
          for it.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Brief Thoughts on Clean Code 2nd Edition — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#brief-thoughts-on-clean-code-2nd-edition</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#brief-thoughts-on-clean-code-2nd-edition</guid>
      <pubDate>Thu, 08 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I read the first edition a few years ago while in school, and while at
          the time I dogmatically adopted those practices, I eventually found a
          less dogmatic style for myself that was certainly shaped by those
          principles. Largely, the first edition had been beaten to death by
          others, so as a result the 2nd edition included a lot of “damage
          control”. Though I think this damage control ultimately brought more
          perspectives into the book which was nice.
        </p><p>
          Perhaps a longer more in-depth review is subject to a dedicated
          writing of mine at some point, so instead I want to make my main
          premise clear on this matter.
        </p><p>
          The way we write code is shaped by our tools, and primarily our
          editors. Given the scale of massively terrible codebases in the wild,
          this isn’t a massive skill issue, but rather one of giving chainsaws
          to an army of monkeys. The real job is to understand and move
          efficiently and deliberately in complex systems, and if our tools
          aren’t helping with that then how do we expect things to improve?
        </p><p>
          Our primary tool for editing code has been fundamentally the same for
          decades, yet the systems we’ve produced have massively scaled in
          required (not accidental) complexity. Today’s editors are only great
          for writing text, not understanding and working with complex systems.
        </p><p>
          The smaller function style is in theory quite a nice idea, because
          small functions tend to describe the overall process far better than
          inlined code. However, we also need to see the details and relate it
          to the higher-level process without being forced to jump around
          everywhere. No editor on the planet lets you see both views at once on
          the same screen, and instead the authoring programmer has to decide
          which one to show you by writing the code in that style.
        </p><p>
          The beginning of the chapter on comments also points this out to an
          extent.
        </p><p>
          <em>”Comments are, at best, a necessary evil. If our programming
            languages were expressive enough, or if you and i had the talent to
            subtly wield those languages to express our intent, we would not
            need comments very muchperhaps not at all.</em>
        </p><p>
          <em>The proper use of comments is to compensate for our failure to
            express ourselves in code. Note that I used the word failure. I
            meant it. Comments are always failures of either our languages or
            our abilities.”</em>
        </p><p>
          If after 5 decades, we still haven’t yet found a universally
          satisfiable way to express ourselves in the languages and tools we
          use, what do you think the problem is?
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>A Blow to Snapshot Testing — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#a-blow-to-snapshot-testing</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#a-blow-to-snapshot-testing</guid>
      <pubDate>Tue, 06 Jan 2026 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Tests that have large outputs to verify (eg. Macro Expansions, Codegen
          Tools,) are tedious to write. In such cases, my go to strategy was to
          always use
          <a href="https://github.com/pointfreeco/swift-snapshot-testing">Snapshot Testing</a>, which would instead capture the output into a file (or even
          inline). Then, subsequent test runs would diff against the snapshot
          output, which would alert you to changes. Of course, you would have to
          scan the initial snapshot manually to ensure it looked correct the
          first time.
        </p><p>
          Ideally, you would break up the code into smaller pieces that work on
          smaller outputs, and can therefore be tested in isolation. Though at
          some point, it is worth it to have that larger test case that ensures
          the whole thing is tied together properly.
        </p><p>
          However, with the advent of agentic coding and LLMs, I’ve found less
          of a need to rely on snapshot testing other than for
          non-deterministic/hard to determine ahead of time output (eg. See
          <code>CactusLanguageModelTests</code> in
          <a href="https://github.com/mhayes853/swift-cactus/blob/main/Tests/CactusTests/LanguageModelTests/CactusLanguageModelTests.swift#L363">Swift Cactus</a>).
        </p><p>
          Here’s a recent test that I would’ve normally written with Snapshot
          Testing.
        </p><pre><code class="lang-swift">
@Test
@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
func `Streams JSON Large Negative Int128 Digits`() throws {
  let json = "-170141183460469231731687303715884105727"
  let expected: [Int128] = [
    0,
    -<span class="ruby"><span class="hljs-number">1</span>,
</span>    -<span class="ruby"><span class="hljs-number">17</span>,
</span>    -<span class="ruby"><span class="hljs-number">170</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316_873</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168_730</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687_303</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316_873_037</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168_730_371</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687_303_715</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316_873_037_158</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168_730_371_588</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687_303_715_884</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316_873_037_158_841</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168_730_371_588_410</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687_303_715_884_105</span>,
</span>    -<span class="ruby"><span class="hljs-number">1_701_411_834_604_692_317_316_873_037_158_841_057</span>,
</span>    -<span class="ruby"><span class="hljs-number">17_014_118_346_046_923_173_168_730_371_588_410_572</span>,
</span>    -<span class="ruby"><span class="hljs-number">170_141_183_460_469_231_731_687_303_715_884_105_727</span>
</span>  ]
  try expectJSONStreamedValues(json, initialValue: Int128(0), expected: expected)
}
        </code></pre><p>Thankfully I didn’t have to write that by hand!</p><br>]]></content:encoded>
    </item>
    <item>
      <title>“Product Dev” vs “Code Purist” — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#-product-dev-vs-code-purist-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#-product-dev-vs-code-purist-</guid>
      <pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’ve seen a split of these 2 personalities come up recently as a
          result of agentic coding gaining more adoption. Though, it seems to me
          that one must identify with one of these camps, and vehemently disavow
          the other one.
        </p><p>
          From the “product dev” perspective, they were tortured by 2 AM
          debugging sessions, and is finally getting their freedom from those
          elitist code purists. Likewise, the code purist perspective revels in
          those debugging sessions, and now has their life energy sucked from
          them been by those god damn LLMs.
        </p><p>
          One of the interesting things is that the “product dev” would’ve had
          their way a long time ago if in the 80s we looked at the 70s more
          critically. Smalltalk programs were incredibly tiny not because
          Smalltalk programmers were geniuses, or because software was simpler.
          The programs were incredibly tiny because programming wasn’t limited
          to the act of writing abstract textual symbols, but rather the entire
          GUI was the programming environment. Today’s level of AI
          sophistication wasn’t ever needed for this overall effect.
        </p><p>
          The code purist also has a point, and I don’t see the act of manually
          writing code disappearing for at least a decent amount of time. Really
          complex programs, especially ones that require performance sensitive
          optimizations will probably need a significant amount of manual labor
          due to the required precision of the code. Also, I find that designing
          APIs in library code is still more effective to do by hand rather than
          through an LLM (though often the LLM can be used to write tests and
          implement the API if you know what you’re doing). Some changes are
          also quicker to do by hand as well depending on your choice of editor
          and how much time you’ve dedicated to mastering your editor’s motions.
        </p><p>
          In other words, I think it’s worth noting a term for “precise coding”.
          The more human involvement in your development process, the more
          precision one gains over the symbolic representation of the system.
        </p><p>
          Though, for most serious programs, the bottleneck is almost never the
          precise code, but rather the overall systems design and architecture.
          Before writing out code as a set of symbols in our editor, there’s
          often a longer period of rumination within one’s head about the system
          itself which takes up most of the time. This judgement is quite
          difficult for an LLM to do well, because it only knows
          well-established patterns whereas serious systems are often trying to
          define their industry in some novel/not well-established manner.
        </p><p>
          Reminder that at the end of the day, what we call LLMs are really just
          incredible at detecting patterns and correlations. You wouldn’t get
          very far if you had to discover novel ideas just by noticing patterns
          in your head because there’s often a necessary intuitive reasoning
          step that you must perform in your head to come at a novel judgement.
        </p><p>
          So which camp do I identify with on a fulfillment level, “product dev”
          or “code purist”?
        </p><p>
          As a self-proclaimed “fledgling systems designer”, I would have to say
          both. To me there’s no room for compromise on either of these camps if
          one wants to build robust systems. Code is often the dominant symbolic
          representation of the system’s runtime, which needs to be maintained
          over time. I also very much care about the societal effect of the
          system, which would be more along the “product dev” lines of thinking.
        </p><p>
          On a fulfillment note, the former bit is why I pay attention to code
          quality even when “moving fast”, and the latter bit is why I enjoy
          design and many of these higher level writings for instance. My entire
          world perspective is a giant relation graph of both those camps, and
          much more.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>iOS Could’ve Been More Expressive — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#ios-could-ve-been-more-expressive</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#ios-could-ve-been-more-expressive</guid>
      <pubDate>Fri, 26 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          The Home Screen layout is a simple grid of app icons and widgets. This
          is easy to use, but the expressive power is incredibly limited.
        </p><p>
          How can one draw relationships between different apps and different
          widgets? Folders exist, but they are merely just another version of
          the Home Screen without widget support. In other words, not very
          expressive.
        </p><p>
          The Home Screen has been the fundamentally been the same since the
          iPhone 2G in 2007. Certainly, 18 years later should be enough time for
          the vast majority of users to learns to use a more expressive
          interface at the cost of an initial learning curve.
        </p><p>
          On the surface level, one may ask why they need to draw a bunch of
          complex relationships between various apps. This kind of question is
          exactly the result of the problem I’m getting at here. It implies that
          users don’t see the point in potential expressive power that lets them
          think unthinkable and creative thoughts!
        </p><p>
          Social media is easy to use, both in creation and consumption, but the
          forms of expression are incredibly limited. Simple video, text, and
          photos have been around for decades, long before personal computing
          was even commercialized. Certainly, a highly interactive multitouch
          display that can now run embedded inference on an enormous corpus of
          information would certainly be able to create new forms of media with
          enhanced expressive power. This didn’t happen, and now we have a
          common term for the end result, “brain rot”.
        </p><p>
          The problem with only focusing on “easy to use” is that it keeps users
          as perpetual beginners. That is, they learn to appreciate the
          simplicity of the interface rather than learning how to use the
          interface to express their inner creative ideas to the fullest
          potential. The latter requires interface design that goes against many
          principles in the HIG (Apple’s human interface guidelines), and that
          embeds a reasonable learning curve into the interface directly (the
          HIG hates this) to learn the complex interactions.
        </p><p>
          This last point is hard to do correctly in today’s climate primarily
          because of the culture, but I think it is possible to pull off
          successfully in a commercialized manner. I will be attempting this
          with Mutual Ink in the coming months. I think the first step is
          creating something that is easy to use like everything else, but
          adding explicit steps where the interface can instruct users directly
          on how to use their expressive power more.
        </p><p>
          Yes, this is “explaining how to use the product” which seems to be
          considered taboo. Yet, great games have been doing this for the
          longest time in subtle ways through signifiers and tutorials that
          blend with the main gameplay, and I think it’s more than doable to
          pull it off in apps too.
        </p><p>
          Another counterpoint is something along the lines of “I just want to
          send an email” or do some task that is considered to be simple. Most
          often, that simple task is just a digitized form of something that was
          previously done in a more physical manner.
        </p><p>
          My take on this is to ask whether or not we should be porting previous
          mediums to a new medium. Email may have once been incredibly useful,
          but is it the best way to communicate on a multitouch medium with
          embedded inference on an enormous corpus of information?
        </p><p>
          What most people really mean when they want to “simple and easy to
          use” is really code word for “I don’t want this thing to be annoying”.
          A well-designed learning curve shouldn’t be annoying, but rather it
          should be fun and engaging!
        </p><p>
          So here’s my controversial take on this whole topic put into a
          singular quote.
        </p><p>
          <em>Simple and easy to use tools that don’t require learning creates a
            culture that despises learning. Do we really want that?</em>
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notes on a Better Commercial Editor (1/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notes-on-a-better-commercial-editor-1-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notes-on-a-better-commercial-editor-1-n-</guid>
      <pubDate>Tue, 23 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          First and foremost, we need to define what makes a better editor than
          existing alternatives. One of my controversial takes is that I believe
          that there hasn’t been a good widespread editor for most software
          development for the past few decades, and that agentic coding isn’t
          the answer to this problem. Today’s editors are fantastic at writing
          text faster, but not so great at creating systems.
        </p><p>
          The true answer to this problem is that each system needs its own
          specific editor, and the system’s designers should be responsible for
          the design of that editor!
        </p><p>
          However, this is antithetical to the way software is commercialized
          and shipped today for a multitude of cultural reasons.
        </p><ol>
          <li>
            We as consumers expect black-boxed “products” and “apps”, not
            malleable tools that we can understand the internals of.
          </li>
          <li>
            We’ll make claims that “building our own editor is too costly and
            time consuming”, and that we need to spend that time shipping faster
            today.
          </li>
        </ol><p>
          However, one trait I’ve seen with some of the best software is that
          often its developers will have built specific tools to assist with its
          development! A good example of this can be seen
          <a href="https://youtu.be/IdpD5QIVOKQ?si=AWT4lRVpDXrT-bR9&amp;t=1164">here</a>.
        </p><p>
          Some of those inherent benefits will be lost when we think of
          commercialization (we have to show something that is ready made!), but
          I think what I’ll present here in these notes is “YC startup worthy”.
          That is, I only plan on showing merely a connection between the
          system, and subsequent parts that edit it. Also, since we’re focused
          on commercialization, I’ll keep the parts that make up the editor
          familiar.
        </p><p>
          There are certainly deeper principles that I haven’t had the chance to
          explore yet, but be my guest if you want to apply to the next batch
          with what you see here.
        </p><p>
          Say we have an app.
          <img src="./global-notes/assets/better-commercial-editor-1-1.png" alt="An app showing a CTA button titled 'Let's Get Started!'">
        </p><p>
          Now let’s right click on the “Let’s Get Started!” button.
          <img src="./global-notes/assets/better-commercial-editor-1-2.png" alt="The app with an instance of the Zed text editor opened beside it.">
        </p><p>
          This opens my code editor of choice (<a href="https://zed.dev/">Zed</a>
          btw) directly to the file and line of where the button was declared.
          In this case, the button is powered by SwiftUI, so I’m taken directly
          to the SwiftUI View containing the button. For the record, both the
          app and Zed need to be in view side-by side.
        </p><p>
          Now as I edit the text for the button. (<code>"Let's Get Started!" -&gt; "Let's Get
            Climbing!"</code>)
          <img src="./global-notes/assets/better-commercial-editor-1-3.png" alt="Editing the text for the button from 'Let's get started!' to 'Let's Get Climbing!'">
        </p><p>
          The app should then update in real time (think hot reload for
          simplicity).
          <img src="./global-notes/assets/better-commercial-editor-1-4.png" alt="The app with the updated button text alongsize Zed.">
        </p><p>
          Now let’s go ahead and select another screen in the app, by perhaps
          dragging downwards at the bottom of the app. (If such an editor
          materializes, we may stop thinking of things in terms of screens for
          the record!)
          <img src="./global-notes/assets/better-commercial-editor-1-5.png" alt="An arrow pointing downwards from the app that links to a section where other app screens can be selected, most notably there's one with a mountain depicted.">
        </p><p>
          Let’s pick the one with the mountain in view because it catches my
          eye.
          <img src="./global-notes/assets/better-commercial-editor-1-6.png" alt="The screen with the mountain depicted alongside another instance of Zed that Stephen was using to edit the system prompt for the 'Climb Readiness' section.">
        </p><p>
          It looks like we had some previous edits from Stephen here, and now I
          can see them. That’s really cool! Looks like he’s editing the system
          prompt for the “Climb Readiness Section”. I wonder if I could see
          changes in real time, it would be really cool to see a live feed of
          him editing the system prompt, and see how that would impact things!
        </p><p>
          Wait a minute it looks like he’s doing just that!
          <img src="./global-notes/assets/better-commercial-editor-1-7.png" alt="As Stephen edits the system prompt, the prompt is tested against the LLM with some sample generations being present in a section below.">
        </p><p>
          Perhaps, we can see what a mockup would look like with one of these
          outputs. Let me select one!
          <img src="./global-notes/assets/better-commercial-editor-1-8.png" alt="After selecting one of the sample generations, we can see a real time mockup of what that generation would look like in the final product.">
        </p><p>Nice! Now I know exactly how it looks in the final product!</p><p>I can keep going, but I think this covers a basic starting point.</p><p>
          One trick question I like to ask other developers is the following.
        </p><p>
          <em>Say we’re working on a codebase for a commercial aircraft system.
            Now let’s assume that we want to find the code for the left engine,
            how should we organize the system to make finding it easier?</em>
        </p><p>I’ll often hear answers such as:</p><p>
          <em>Let’s put it in a clear module with a clear name somewhere in the
            repo, and let’s make the folder structure easy to parse so that one
            could find the core modules easily.</em>
        </p><p>My answer is simple.</p><p>
          <em>Why can’t we find it on the left engine of an actual plane?</em>
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>I’ve Been Writing a Lot of Notes About AI Lately — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#i-ve-been-writing-a-lot-of-notes-about-ai-lately</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#i-ve-been-writing-a-lot-of-notes-about-ai-lately</guid>
      <pubDate>Mon, 22 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Of course, not the kind that actually goes into the real technical
          details. You can checkout
          <a href="https://github.com/cactus-compute/cactus">cactus</a> (and the
          <a href="https://github.com/mhayes853/swift-cactus">Swift client</a> I
          maintain) for that, but rest assured that I want to focus future notes
          on those details.
        </p><p>
          The biggest shift in recent times is adopting agentic coding into my
          workflow, but also because Richard Hamming says that one cannot afford
          to <em>not</em> have a stance on AI. I think this is at least 4x true
          in today’s landscape compared to 1997 when he wrote that in his book
          <em>The Art of Doing Science and Engineering</em>.
        </p><p>
          That being said, I don’t use any AI for my writings, and especially
          these notes. The reason for that is because these notes are primarily
          for me, and are designed for my further understanding of various
          topics. Using AI to generate such writings goes against the entire
          point of me doing them. (Also the fact that I want to seem genuine,
          and that I’m intentionally not making any money off these writings.)
        </p><p>
          That being said, I want to keep further notes less related to the
          societal engineering implications of AI, and more on the technical
          details. I’ll also have an article up at some point that puts my
          perspective on the societal engineering AI landscape into a few short
          sentences so that one can get the gist and move on.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notes on Non-Technical AI Culture — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notes-on-non-technical-ai-culture</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notes-on-non-technical-ai-culture</guid>
      <pubDate>Sun, 21 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          My opinions on how current AI tools are used mostly relate to software
          development and UI/UX design, and I’ll admit that I haven’t addressed
          other creative fields like art.
        </p><p>
          One thing I’ve noticed is quite the difference in tone between
          software development and non-software development fields. In my case,
          I feel like <em>not using</em> AI is starting to become more and more
          like a sin against humanity, and if anything I’ve felt more guilty for
          <em>not using it</em>. Perhaps that’s because the software industry
          loves fast shipping speeds (probably way too much), and there’s a
          sense of FOMO and social pressure from not shipping faster using AI.
        </p><p>
          Yet, when I look into more non-software creative fields, I see the
          exact opposite culture. Using AI is essentially a sin against humanity
          in these other fields (especially art), or at least that’s the
          sentiment I’m getting. For instance, I don’t know of a similar
          <a href="https://bsky.app/profile/did:plc:dc63oncxgxguompva43fl4u2">social media account</a>
          that’s literally tagging every piece of commercial software for
          generative AI usage.
        </p><p>
          To be honest, the list would be incredibly massive, and you can start
          by adding all the companies
          <a href="https://www.ycombinator.com/companies?batch=Fall%202025">here</a>
          and work your way down the other recent batches if you want to create
          such a list. Then make your way to big tech. Of course, many tech
          companies forbid the usage of AI, but most major ones are pushing it.
          It’s not unlikely that the software used to produce these notes has a
          significant amount of AI generated code somewhere, and likewise for
          creating content as a part of the ongoing AI boycotts.
        </p><p>
          Going back to non-software creative fields, I can understand the
          sentiment against AI. The point of creation is not to let a machine
          generate a bunch of variants, and then to have a human or other AI
          agent pick the best one. That defeats the whole purpose of creation,
          and the speed gains from such a method are more likely to be
          short-term and illusory. Partially because the creative process opens
          new forms of understanding that are lost when the creation is done for
          you, and also partially because creatives actually like their work and
          didn’t sign up to become managers.
        </p><p>
          In fact, there will likely be significant negative outcomes if we make
          everyone’s job a manager of some kind. For instance, in software
          development there was plenty of research before the AI-era that shows
          how code review was one of the worst places to catch bugs, despite
          developers thinking otherwise. In effect, making all developers “code
          reviewers” is likely to produce <em>worse</em> outcomes over time, not
          better. I imagine the same can be applied to other creative fields.
        </p><p>
          I will note is that those who use AI uncritically will not be ahead
          for very long if we do things correctly. Partially, this will come
          about when it takes human ingenuity to differentiate a product from
          the competition, and also partially because actual creatives can use
          existing AI tools far more effectively than those without those
          skills.
        </p><p>
          I’m also going to predict that the ongoing AI boycotts will likely
          have little to no effect on the pace of change going on. There are
          currently trillions of dollars being pushed into generative AI, and
          even an economic bubble burst will likely not entirely stop that
          funding in the long term just like it didn’t stop for the web. For
          better or for worse, it’s here to stay.
        </p><p>
          As always, I’m going to reiterate and state that the tools drive the
          culture. AI itself has many uses that can enhance the overall output
          of creatives, but the tools have to encourage a style of thinking that
          provide those enhancements instead of automating all creation. This is
          a far more important problem that has devastating consequences if not
          handled properly.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Agentic Coding Initial Thoughts — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#agentic-coding-initial-thoughts</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#agentic-coding-initial-thoughts</guid>
      <pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Having played around with the Codex CLI for a week now, it’s quite
          safe to assume that adopting AI code generation tools will more or
          less be required in the future, so resisting is probably not something
          that’s viable in the long term. Generally speaking, getting AI to
          generate good code still requires that you know how to implement
          things yourself, because you will need to dictate your implementation
          strategy to the agent somehow.
        </p><p>
          Some people say that the job of a software engineer will shift more to
          that of a product manager. This is not the vibe I’m getting so far
          when adopting these tools, and I certainly don’t want it to become
          reality either. In order to get good results with AI generated code,
          I’ve still had to dictate precisely how the agent should implement a
          set of functionallity down to the APIs it should invoke and files it
          should edit.
        </p><p>
          Overall, I’ve found myself doing a lot more writing on how to
          implement something, rather than going back and forth constantly on
          the next line of code that I’m typing. This is where the productivity
          increase comes from. Instead of tediously writing every individual
          line of code, you’ll instead write a paragraph or 2 detailing the
          implementation in plain english. (eg. Instead of implementing a
          depth-first-traversal by hand, I’ll just tell the agent to do a
          depth-first-traversal.)
        </p><p>
          The resulting code is usually acceptable on the first generation for
          most things, but often I’ll make small manual tweaks regardless for
          future proofing scenarios.
        </p><p>
          I’ll now detail my general playbook for implementing a simple feature.
        </p><ol>
          <li>
            We start with getting the agent to generate tests for a specifc API
            or functionallity. I detail exactly what tests to write in plain
            english, and explicitly tell the model to not implement the
            functionallity for any of the tests. We do not move on to step 2
            until a solid set of tests have been created.
          </li>
          <li>
            We’ll move onto the implementation of the feature. In doing this,
            I’m very explicit in the sense that I tell the agent how to
            implement the feature as I would normally do it in code. The key
            difference is that I’m handing away the typing part to the agent.
          </li>
        </ol><p>
          In both steps, I generally will make small manual edits to the
          generated code, so I don’t think manual coding is dead atm. Think of
          writing a for-loop when coding manually, you’ll write the code for a
          single iteration, and the for-loop will execute it N times. The agent
          is generall a for-loop for code generation, you may handwrite an
          explicit example, and the agent will be able to figure out the N
          stylistic variants you need.
        </p><p>
          If anything, you’ll need to be a lot better at writing code now more
          than ever. Your tastes, styles, and mannerisms now matter a lot more,
          because you’ll more or less be instructing the agent on how to mass
          produce them.
        </p><p>
          I now want to take a moment to address the culture around agentic
          coding, of which I believe is quite depressing and is more so a
          problem than any of the existing tools.
        </p><p>
          There seems to be 2 camps of thought, an anti-agent camp, and a total
          vibe-coding camp. The anti-agent camp will tend to uncritically look
          at how other developers use AI, and will simply just state that all
          AI-generated code is bad. The vibe-coding camp tends to believe that
          all developers will be out of a job in the near future, and that
          you’ll be “left behind” if you refuse to adopt these tools.
        </p><p>
          Sooner or later, adoption of these tools will probably become a
          requirement, and one that pure vibe-coders will actually not find as
          useful to them as they think. In fact, pure vibe-coders are probably
          in a worse overall state (assuming they don’t lean on a prior domain
          of critical thought), and I believe one of 3 scenarios will happen.
          All 3 end with pure vibe coders losing out to people who are dedicated
          to their craft.
        </p><ol>
          <li>
            Vibe coding in its current form becomes economically unsustainable,
            and thus the cost of doing things the pure vibe-coding way shoots
            way through the roof.
          </li>
          <li>
            Vibe coding becomes ubiquitous. In order for your product to stand
            out from the competition, you’ll be forced to go beyond the
            capabilities of pure vibe-coding and into serious development.
          </li>
          <li>
            More Software Engineers and technically inclined people begin
            adopting these tools in droves and use their knowledge and
            experience to produce far better outputs than vibe coders.
          </li>
        </ol><p>
          I think scenarios 2 and 3 are more likely to happen, but elements of
          scenario 1 could arise depending on the economics of the bubble.
          Though also take note that all 3 scenarios do require general adoption
          of these tools, and that more or less everyone will have to use them
          at some point. (Though, I don't think we'll be at the "left behind"
          stage for some time.)
        </p><p>
          In other words, those who can think critically with these tools will
          do far better than those who were early adopters, but otherwise lack
          critical thinking ability. Even though the uncritical people may be
          ahead for some time, history shows that things eventually stabilize.
        </p><p>
          Now my real thoughts on what critical thinking culture the tools
          create is a far more important question, and far more important
          problem IMO. This is where we’re currently struggling, and the long
          term effects can be disastrous if not managed correctly.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Fooling Ourselves — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#fooling-ourselves</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#fooling-ourselves</guid>
      <pubDate>Thu, 18 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          If you watch Alan Kay’s talks, you’ll often hear the idea that we pay
          to be fooled in theater. This is also the case for TV, and most
          definitely social media.
        </p><p>
          Another interesting thought is that we also fool the brain during
          surgery. Even if our literal body is being operated on in a very
          gruesome manner, the brain proceeds with thought like everything is
          normal!
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Thoughts on TUIs — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#thoughts-on-tuis</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#thoughts-on-tuis</guid>
      <pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          It seems that we’re seeing more and more TUIs as of late, and
          personally I’ve been experimenting with agentic coding using the Codex
          CLI which uses a TUI. Claude Code and Open Code are also using such a
          TUI for their UI, and I’ve even seen a
          <a href="https://github.com/whyisdifficult/jiratui">Jira TUI</a>
          floating around.
        </p><p>
          My unapologetic opinion still remains that the terminal is perhaps one
          of the worst UI designs that has continually stuck around, despite its
          efficiencies comapred to GUIs. The main reason for such efficiencies
          over the GUI is because GUI applications are designed to be completely
          siloed and isolated from each other! On the other hand, modern shells
          generally abide by the UNIX philosophy of composable and small
          programs.
        </p><p>
          This is a powerful idea! Smalltalk systems did it as well for GUIs in
          the 70s! (<a href="https://youtu.be/AnrlSqtpOkw?si=vPdCNiqOu8WR2G98&amp;t=595">This</a>
          is one of the coolest demos on how this could be done.)
        </p><p>
          Unfortunately, companies behind the major <em>consumer</em> (sorry
          linux) desktop operating systems (Apple, Microsoft) missed the
          composition idea, and we’re still stuck with the result today. Of
          course, we’re also still stuck with the UNIX terminals of the past
          today, which is why often they are more efficient to use than modern
          GUI applications.
        </p><p>
          However, that reasoning doesn’t explain my dislike for the terminal’s
          UI design. The simple answer to that is a lack of visibility and
          feedback. For instance, as you type
          <code>rm -drf /some-important-directory</code> nothing warns you that
          you are about to nuke critical data <em>as you’re typing</em>. You
          only find out what happens after you run the command (hopefully you
          have proper permissions in place)! This lack of feedback has no doubt
          led to many instances of dropping tables in production databases, or
          similarly destructive acts in production environments!
        </p><p>
          Of course, this is not even mentioning the fact that it takes rote
          memorization to even know what commands you have at your disposal in
          the first place. The terminal doesn’t offer any sort of environment to
          learn them either. Therefore, you usually you’ll end up finding them
          online or in videos like
          <a href="https://www.youtube.com/watch?v=XhLMS47l8Bw">this</a>.
        </p><p>
          As I type a prompt into the Codex TUI, I get absolutely zero feedback
          on what effect that prompt will have until I actually submit it to the
          agent. Given that as serious programmers (not vibe coders), we often
          need to explicitly guide the agent by telling it how to implement
          things, this lack of feedback can get quite intolerable as
          implementation details must be kept in one’s head.
        </p><p>
          For the record, most chatbot UIs are generally not much better than
          the terminal either. ChatGPT is essentially the same thing, because
          you’re entering a prompt into a tiny text box that offers no feedback
          until you submit the prompt. ChatGPT is designed to do almost exactly
          as you say with little to no room for pushback (outside of loosely
          defined guardrails), which if used incorrectly can further cognitive
          biases (eg. Look at the agreeableness phenomenon). It’s basically a
          glorified terminal for AI inference!
        </p><p>
          People seem to like TUIs because they often don’t suffer the same
          complexities or performance issues found in traditional GUIs. I say we
          should just make GUIs that aren’t just glorified command centers. The
          GUI was meant to be an explorable medium for learning and not a
          command center for poor thought. Regardless, I think this TUI trend
          highlights an important aspect of GUIs that we at large haven’t been
          taking advantage of, or much less even thought of in the first place.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Some Planned Upcoming Writings — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#some-planned-upcoming-writings</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#some-planned-upcoming-writings</guid>
      <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          With tentative titles, organized by the sections you see on my home
          page.
        </p><p>* = In Progress</p><p><strong>New Mediums</strong></p><ul>
          <li>
            Locality of Decisions*
            <ul>
              <li>
                Goes over the disasters of one-size-fits-all decision making,
                and what we need to do to move away from this paradignm.
              </li>
            </ul>
          </li>
          <li>
            Redesigning Code Editors, A Conceptual Overview
            <ul>
              <li>
                I’ve written quite a few notes on why I believe modern code
                editors are quite bad at building systems, but great at writing
                text. Though, I’ve yet to show any concrete examples of what a
                “better editor” looks like (Hint: Agentic coding in its
                widespread form isn’t the solution).
              </li>
            </ul>
          </li>
          <li>
            Flying around the Aspects of Abstraction
            <ul>
              <li>
                An alternative POV to Bret Victor’s
                <a href="https://worrydream.com/LadderOfAbstraction/">Up and Down the Ladder of Abstraction</a>.
              </li>
            </ul>
          </li>
          <li>
            Mutual Ink, Creative Software and The Collaborative Interface*
            <ul>
              <li>
                Depending on how things go, I may merge the stuff on code
                editors into this piece. Mutual Ink is an app that I’m currently
                working on in my free time that attempts to make LLMs (and
                particularly local ones) be seen as tools that can help us think
                better rather than take the thinking from us.
              </li>
            </ul>
          </li>
        </ul><p><strong>Building Products and Tools</strong></p><ul>
          <li>
            How I used the Physical World to Enhance App Design
            <ul>
              <li>
                Goes over an experience I had recently in developing Mutual Ink
                in which I laid out a grid of objects on my bed to design a
                fault-tolerant and iCloud syncable navigation system.
              </li>
            </ul>
          </li>
          <li>
            The Agent Framework, Why Cactus is Better than FoundationModels*
            <ul>
              <li>
                I’ve already shipped low level Swift bindings to work with LLMs
                powered by the Cactus inference engine. The engine itself is
                very fast, efficient, optimized specifically for ARM, and
                supports many different models.
              </li>
              <li>
                However, FoundationModels, though less technically impressive,
                has an API that’s much easier to use, and thus will likely see
                more adoption despite its flaws.
              </li>
              <li>
                This later point is why I’m currently developing an agent
                framework for Cactus in my free time. The TLDR is that the
                vision is “SwiftUI for dynamic inference and processes”. That
                is, creating a traditional multi-step agent workflow is at the
                same level of difficulty as creating a SwiftUI view.
              </li>
            </ul>
          </li>
        </ul><br>]]></content:encoded>
    </item>
    <item>
      <title>Qwen3 0.6b vs Swift Strings — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#qwen3-0-6b-vs-swift-strings</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#qwen3-0-6b-vs-swift-strings</guid>
      <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<pre><code><span class="hljs-attribute">
Difference</span>: …

<span class="stata">       <span class="hljs-string">""</span>"
    &lt;think&gt;
    Okay, the user is asking <span class="hljs-keyword">about</span> how to make a pizza. I need to <span class="hljs-keyword">break</span> down the steps <span class="hljs-keyword">in</span> a <span class="hljs-keyword">clear</span> way. First, they might be looking <span class="hljs-keyword">for</span> a simple recipe. Let me start <span class="hljs-keyword">by</span> outlining the basic ingredients: dough, toppings, cheese, sauce. Then <span class="hljs-keyword">list</span> each step <span class="hljs-keyword">in</span> <span class="hljs-keyword">order</span>—prepping the dough first <span class="hljs-keyword">for</span> a good texture.
   
    Wait, but some people prefer a different approach. Maybe start with mixing ingredients instead of just preparing dough? Or is there something specific they want <span class="hljs-keyword">in</span> terms of steps? The user hasn't specified preferences, <span class="hljs-keyword">so</span> I should <span class="hljs-keyword">keep</span> it general but structured.
   
    I should mention key points like kneading the dough properly before adding toppings. Also, ensuring the sauce is well mixed and combined with cheese properly. Maybe add tips <span class="hljs-keyword">on</span> storage <span class="hljs-keyword">if</span> they're making it ahead of time.
   
    Is there anything <span class="hljs-keyword">else</span> I should <span class="hljs-keyword">include</span> that's common <span class="hljs-keyword">in</span> pizza-making? Like optional variations or checking <span class="hljs-keyword">for</span> any mistakes to avoid—like not using the right sauce or mixing too much cheese. That would make it a complete guide.
    &lt;/think&gt;
   
    Here’s a step-<span class="hljs-keyword">by</span>-step guide to making a classic pizza:
   
    ### **Ingredients:**
    - 1 cup all-purpose flour
    - 1/4 cup water
    - 2 cups milk (or almond milk)
    - 1 egg (<span class="hljs-keyword">for</span> mixing)
    - Salt and pepper (<span class="hljs-keyword">as</span> needed)
   
    ---
   
    ### **Steps:**
   
    #### **Step 1: Prepare the Dough**
    Mix all dry ingredients together <span class="hljs-keyword">in</span> a bowl. Add wet ingredients slowly to avoid lumps—mix until you get a <span class="hljs-keyword">smooth</span> dough.
   
    #### **Step 2: Knead the Dough**
    Knead <span class="hljs-keyword">for</span> <span class="hljs-keyword">about</span> *3 minutes* until it becomes elastic, tender, and has a good texture. <span class="hljs-keyword">If</span> it’s too flat or dry, add <span class="hljs-keyword">more</span> water or milk <span class="hljs-keyword">as</span> needed.
   
    #### **Step 3: Roll <span class="hljs-keyword">Out</span> (Optional)**
    <span class="hljs-keyword">If</span> making pizza dough ahead of time:
    - Let rest <span class="hljs-keyword">for</span> *1 hour*, then roll <span class="hljs-keyword">out</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">an</span> unshaken surface (like parchment paper) with your hands or machine to achieve even thickness.
   
    ---
   
    ### **Step 4: Add Toppings**
    - Spread cheese evenly over the rolled-<span class="hljs-keyword">out</span> dough.
    - Top with fresh tomato sauce, cheese slices, pepperoni slices (<span class="hljs-keyword">if</span> using), mushrooms, onions <span class="hljs-keyword">if</span> added before mixing the sauce.
   
    ### **Step 5: Mix Sauce and Cheese**
    <span class="hljs-keyword">If</span> you want a tangy base:
    1. <span class="hljs-keyword">In</span> a bowl, mix together tomato sauce and cheese until combined.
    2. <span class="hljs-keyword">Use</span> this mixture to cover the toppings.
   
    ---
   
    ### **Step 6: Bake**
    Place <span class="hljs-keyword">in</span> preheated oven at *375°F* (190°C). Bake <span class="hljs-keyword">for</span> *8–10 minutes*, then flip or serve immediately.
   
    ---
   
    ### **Tips:**
    - Store leftovers <span class="hljs-keyword">in</span> <span class="hljs-keyword">an</span> air-tight container <span class="hljs-keyword">for</span> up to *4 days*.
    - <span class="hljs-keyword">If</span> making pizza dough ahead of time, store it <span class="hljs-keyword">in</span> the fridge <span class="hljs-keyword">for</span> less than 2 hours before <span class="hljs-keyword">rolling</span> <span class="hljs-keyword">out</span> again.
   
  − Enjoy your homemade pizza! ���
  + Enjoy your homemade pizza! 🍕
    <span class="hljs-string">""</span>"

(First: −, Second: +)</span>
        </code></pre><br>]]></content:encoded>
    </item>
    <item>
      <title>Clean Code == Good UI Design (3/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#clean-code-good-ui-design-3-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#clean-code-good-ui-design-3-n-</guid>
      <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’ve been rumaging around through the 2nd edition of the Clean Code
          book (the first 2 parts of this series were written prior to me having
          knowledge of the 2nd edition), and made it to the first code example
          in the book which has to do with roman numerals.
        </p><p>This is the “unclean” version.</p><pre><code class="lang-java">
package fromRoman;

<span class="hljs-keyword">import</span> java.util.Arrays;

public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FromRoman</span> {</span>
  public static int convert(String roman) {
    <span class="hljs-keyword">if</span> (roman.contains(<span class="hljs-string">"VIV"</span>) ||
      roman.contains(<span class="hljs-string">"IVI"</span>) ||
      roman.contains(<span class="hljs-string">"IXI"</span>) ||
      roman.contains (<span class="hljs-string">"LXL"</span>) ||
      roman.contains (<span class="hljs-string">"XLX"</span>) ||
      roman.contains(<span class="hljs-string">"XCX"</span>) ||
      roman.contains (<span class="hljs-string">"DCD"</span>) ||
      roman.contains (<span class="hljs-string">"CDC"</span>) ||
      roman.contains (<span class="hljs-string">"MCM"</span>)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
    }
    roman = roman.replace (<span class="hljs-string">"IV"</span>, <span class="hljs-string">"4"</span>);
    roman = roman.replace (<span class="hljs-string">"IX"</span>, <span class="hljs-string">"9"</span>);
    roman = roman.replace (<span class="hljs-string">"XL"</span>, <span class="hljs-string">"F"</span>);
    roman = roman.replace (<span class="hljs-string">"XC"</span>, <span class="hljs-string">"N"</span>);
    roman = roman.replace (<span class="hljs-string">"CD"</span>, <span class="hljs-string">"G"</span>);
    roman = roman.replace (<span class="hljs-string">"CM"</span>, <span class="hljs-string">"0"</span>);
    <span class="hljs-keyword">if</span> (roman.contains <span class="hljs-string">"IIII"</span>) ||
      roman.contains (<span class="hljs-string">"VV"</span>) ||
      roman.contains (<span class="hljs-string">"XXXX"</span>) ||
      roman.contains (<span class="hljs-string">"LL"</span>) ||
      roman.contains (<span class="hljs-string">"CCCC"</span>) ||
      roman.contains (<span class="hljs-string">"DD"</span>) ||
      roman.contains (<span class="hljs-string">"MMMM"</span>)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
    }
    int[] numbers = <span class="hljs-keyword">new</span> int [roman.length()];
    int i = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (char digit : roman.toCharArray ()) {
      <span class="hljs-keyword">switch</span> (digit) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'I'</span><span class="hljs-function"> -&gt;</span> numbers [i] = <span class="hljs-number">1</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'V'</span><span class="hljs-function"> -&gt;</span> numbers [i] = <span class="hljs-number">5</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'X'</span><span class="hljs-function"> -&gt;</span> numbers[il = <span class="hljs-number">10</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'L'</span><span class="hljs-function"> -&gt;</span> numbers [i] = <span class="hljs-number">50</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'C'</span><span class="hljs-function"> -&gt;</span> numbers [i] = <span class="hljs-number">100</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'D'</span><span class="hljs-function"> -&gt;</span> numbers [i] = <span class="hljs-number">500</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'M'</span><span class="hljs-function"> -&gt;</span> numbers[i] = <span class="hljs-number">1000</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'9'</span><span class="hljs-function"> -&gt;</span> numbers[i] = <span class="hljs-number">9</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'F'</span><span class="hljs-function"> -&gt;</span> numbers[il = <span class="hljs-number">40</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'N'</span><span class="hljs-function"> -&gt;</span> numbers[i] = <span class="hljs-number">90</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'G'</span><span class="hljs-function"> -&gt;</span> numbers[il = <span class="hljs-number">400</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'O'</span><span class="hljs-function"> -&gt;</span> numbers|i] = <span class="hljs-number">900</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'4'</span><span class="hljs-function"> -&gt;</span> numbers|i] = <span class="hljs-number">4</span>;
        <span class="hljs-keyword">default</span><span class="hljs-function"> -&gt;</span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
      }
      i++;
    }
    int lastDigit = <span class="hljs-number">1000</span>;
    <span class="hljs-keyword">for</span> (int number: numbers) {
      <span class="hljs-keyword">if</span> (number &gt; lastDigit) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
      }
      lastDigit = number;
    }
    <span class="hljs-keyword">return</span> Arrays.stream(numbers).sum();
  }
}
        </code></pre><p>This is the “clean” version.</p><pre><code class="lang-java">
<span class="hljs-keyword">package</span> fromRoman;
<span class="hljs-keyword">import</span> java.util.Arraylist;
<span class="hljs-keyword">import</span> java.util.List;
<span class="hljs-keyword">import</span> java.util.Map;

<span class="hljs-keyword">public</span> class FromRoman {
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">String</span> roman;
  <span class="hljs-keyword">private</span> List&lt;Integer&gt; numbers = <span class="hljs-keyword">new</span> Arraylist&lt;&gt;();
  <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> charIx;
  <span class="hljs-keyword">private</span> <span class="hljs-built_in">char</span> nextChar;
  <span class="hljs-keyword">private</span> Integer nextValue;
  <span class="hljs-keyword">private</span> Integer value;
  <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> nchars;
  Map&lt;Character, Integer&gt; values = Map.of(
    <span class="hljs-string">'I'</span>, <span class="hljs-number">1</span>,
    <span class="hljs-string">'V'</span>, <span class="hljs-number">5</span>,
    <span class="hljs-string">'X'</span>, <span class="hljs-number">10</span>,
    <span class="hljs-string">'L'</span>, <span class="hljs-number">50</span>,
    <span class="hljs-string">'C'</span>, <span class="hljs-number">100</span>,
    <span class="hljs-string">'D'</span>, <span class="hljs-number">500</span>,
    <span class="hljs-string">'M'</span>, <span class="hljs-number">1000</span>
  );

  <span class="hljs-keyword">public</span> FromRoman(<span class="hljs-keyword">String</span> roman) {
    <span class="hljs-keyword">this</span>.roman = roman;
  }

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">int</span> convert(<span class="hljs-keyword">String</span> roman) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> FromRoman(roman).doConversion();
  }

  <span class="hljs-keyword">private</span> <span class="hljs-built_in">int</span> doConversion() {
    checkInitialSyntax();
    convertLettersToNumbers();
    checkNumbersInDecreasingOrder();
    <span class="hljs-keyword">return</span> numbers.stream().reduce(<span class="hljs-number">0</span>, Integer:: sum);
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> checkInitialSyntax() {
    checkForIllegalPrefixCombinations();
    checkForImproperRepetitions();
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> checkForIllegalPrefixCombinations() {
    checkForIllegalPatterns (
      <span class="hljs-keyword">new</span> <span class="hljs-keyword">String</span>[]{<span class="hljs-string">"VIV"</span>, <span class="hljs-string">"IVI"</span>, <span class="hljs-string">"IXI"</span>, <span class="hljs-string">"IXV"</span>, <span class="hljs-string">"IXI"</span>, <span class="hljs-string">"XIX"</span>, <span class="hljs-string">"XCX"</span>, <span class="hljs-string">"XCL"</span>, <span class="hljs-string">"DCD"</span>, <span class="hljs-string">"CDC"</span>, <span class="hljs-string">"CMC"</span>, <span class="hljs-string">"CMD"</span>}
    );
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> checkForImproperRepetitions() {
    checkForIllegalPatterns(
      <span class="hljs-keyword">new</span> <span class="hljs-keyword">String</span>[]{<span class="hljs-string">"IIII"</span>, <span class="hljs-string">"VV"</span>, <span class="hljs-string">"XXXX"</span>, <span class="hljs-string">"LL"</span>, <span class="hljs-string">"CCCC"</span>, <span class="hljs-string">"DD"</span>, <span class="hljs-string">"MMMM"</span>}
    )
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> checkForIllegalPatterns(<span class="hljs-keyword">String</span>[] patterns) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">String</span> badstring : patterns)
      <span class="hljs-keyword">if</span> (roman.contains (badstring))
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException (roman);
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> convertlettersToNumbers() {
    <span class="hljs-built_in">char</span>[] chars = roman.toCharArray();
    nchars = chars. length;
    <span class="hljs-keyword">for</span> (charIx = <span class="hljs-number">0</span>; charIx &lt; nchars; charIx++) {
      nextChar = isLastChar() ? <span class="hljs-number">0</span>: chars[charIx + <span class="hljs-number">1</span>];
      nextValue = values.<span class="hljs-built_in">get</span>(nextChar);
      <span class="hljs-built_in">char</span> thisChar = chars[charIx];
      value = values.<span class="hljs-built_in">get</span>(thisChar);
      <span class="hljs-keyword">switch</span> (thisChar) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'I'</span> -&gt; addvalueConsideringPrefix(<span class="hljs-string">'V'</span>, <span class="hljs-string">'X'</span>);
        <span class="hljs-keyword">case</span> <span class="hljs-string">'X'</span> -&gt; addValueConsideringPrefix(<span class="hljs-string">'L'</span>, <span class="hljs-string">'C'</span>);
        <span class="hljs-keyword">case</span> <span class="hljs-string">'C'</span> -&gt; addValueConsideringPrefix(<span class="hljs-string">'D'</span>, <span class="hljs-string">'M'</span>);
        <span class="hljs-keyword">case</span> <span class="hljs-string">'V'</span>, <span class="hljs-string">'I'</span>, <span class="hljs-string">'D'</span>, <span class="hljs-string">'M'</span> -&gt; numbers.<span class="hljs-built_in">add</span>(value);
        <span class="hljs-keyword">default</span> -&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
      }
    }
  }

  <span class="hljs-keyword">private</span> <span class="hljs-built_in">boolean</span> islastChar() {
    <span class="hljs-keyword">return</span> charIx + <span class="hljs-number">1</span> == nchars;
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> addValueConsideringPrefix(<span class="hljs-built_in">char</span> pl, <span class="hljs-built_in">char</span> p2) {
    <span class="hljs-keyword">if</span> (nextChar == pl || nextChar == p2) {
      numbers.<span class="hljs-built_in">add</span>(nextValue - value);
      charIx++;
    } <span class="hljs-keyword">else</span> numbers.<span class="hljs-built_in">add</span> (value);
  }

  <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> checkNumbersInDecreasingOrder() {
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; numbers.<span class="hljs-built_in">size</span>() - <span class="hljs-number">1</span>; i++)
      <span class="hljs-keyword">if</span> (numbers.<span class="hljs-built_in">get</span>(i) &lt; numbers.<span class="hljs-built_in">get</span>(i + <span class="hljs-number">1</span>))
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidRomanNumeralException(roman);
  }
}
        </code></pre><p>And this is “Future Bob’s” comments on the “clean” version.</p><blockquote>
          <p>
            Two months later I'm torn. The first version, ugly as it was,
            was not as chopped up as this one. It's true that the names and
            the ordering of the extracted functions read like a story and are a
            big help in understanding the intent; but there were several times
            that I had to scroll back up to the top to assure myself about the
            types of instance variables. I found the choppiness, and the
            scrolling, to be annoying. However, and this is critical, I am
            reading this cleaned code after having first read the ugly version
            and having gone through the work of understanding it. So now, as I
            read this version, I am annoyed because I already understand it and
            find the chopped-up functions and the instance variables redundant.
          </p>
          <p>
            Don't get me wrong, I still think the cleaner version is better.
            I just wasn't expecting the annoyance. When I first cleaned it,
            I thought it was going to be annoyance free.
          </p>
          <p>
            I suppose the question you should ask yourself is which of these two
            pieces of code you would rather have read first. Which tells you
            more about the intent? Which obscures the intent?
          </p>
          <p>Certainly the latter is better in that regard.</p>
          <p>
            This annoyance is an issue that John Ousterhout and I have debated.
            When you understand an algorithm, the artifacts intended to help you
            understand it become annoying. Worse, if you understand an
            algorithm, the names or comments you write to help others will be
            biased by that understanding and may not help the reader as much as
            you think they will. A good example of that, in this code, is the
            <code>addValueConsideringPrefix</code> function. That name made
            perfect sense to me when I understood the algorithm. But it was a
            bit jarring two months later. Perhaps not as jarring as
            <code>49FNGO</code>, but still not quite as obvious as I had hoped
            when I wrote it. It might have been better written as
            <code>numbers.add (decrementValueIfThisCharIsaPrefix);</code> ,
            since that would be symmetrical with the
            <code>numbers.add(value);</code> in the nonprefixed case.
          </p>
          <p>
            The bottom line is that your own understanding of what you are
            cleaning will work against your ability to communicate with the next
            person to come along. And this will be true whether you are
            extracting well-named methods, or adding descriptive comments.
            Therefore, take special care when choosing names and writing
            comments; and don't be surprised if others are annoyed by your
            choices. Lastly, a look after a few months can be both humbling and
            profitable.
          </p>
        </blockquote><p>
          It’s true that the “cleaned” version does a better job at describing
          the overall <em>process</em> of what is actually going on here,
          especially if you can read the entire thing on one screen. However,
          modern editors will not show you the entirety of the code all at once
          (rather only ~50 lines at a time), hence the scrolling annoyance.
        </p><p>
          When you scroll, you have to keep the context you can’t see in your
          head. Given that code is a precise artifact, you’ll find that you
          can’t easily hold the code for entire functions in your head. This
          will cause you to constantly stumble and force you to refresh the
          knowledge by scrolling back up to the previous code (or by jumping to
          another file in some cases).
        </p><p>
          The interesting thing is that in UI design circles, the code would be
          seen as information that needs to be presented with a more clear
          visual hierarchy. Thus, the solution would be to find a way to present
          the literal code itself in a much more intuitive manner (ie. Don’t
          hide the important parts by default!).
        </p><p>
          In programming circles, we simply blame the programmer for poor UI
          design choices of the editor, and tell them to refactor.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notes on Vibe Coding for Software Engineers — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notes-on-vibe-coding-for-software-engineers</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notes-on-vibe-coding-for-software-engineers</guid>
      <pubDate>Mon, 08 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Most people using vibe coding tools like Lovable or Bolt are not
          software engineers, but rather more ordinary people with ideas (there
          just aren’t 10s of millions of software engineers in the world that
          would all willingly use those tools lol). I’m not addressing those
          people with these notes, but rather us who aspire to or write more
          critical software systems.
        </p><p>
          First and foremost, the biggest problem currently with these tools for
          those trying to build systems as that the tools aren’t designed to
          augment thinking but rather automate creation. From a systems
          understanding standpoint, this can be disastrous, and as such it’s
          hard to use these tools directly for systems understanding purposes.
          This is quite a let down, and is something that I hope to address
          through future work.
        </p><p>
          However, that doesn’t mean these tools are absolutely useless, and
          surely they do make one “more productive” if used correctly. By “more
          productive”, I don’t necessarily mean just a faster shipping pace, but
          rather a combination of speed and enhanced output (ie. Less “breaking
          things” while keeping the “moving fast” part). The enhanced output
          part is what we need to focus on, and is what can make us stand out
          compared to just those who focus on speed.
        </p><p>
          The key thing to note is that right now the tools have been primarily
          focused on code generation, but for most technical work that’s maybe
          ~10%-20% of the entire battle. A lot more work is needed to
          “understand and design processes” in a specified environment which
          includes, but is not solely limited to the programming languages used
          in the system.
        </p><p>
          Of course, if you repeatedly implement the same or highly similar
          simple technical designs (eg. Simple CRUD operations, UI components,
          etc.) over and over again for different features or systems, this
          repetition is ripe for automation with AI. Even such, you still need
          to spend time understanding exactly what was generated to avoid
          problems down the line.
        </p><p>
          In 2024 (before the term “vibe coding” was coined) I spent the later
          part of 6-8 weeks building and refining an internal tool for test
          automation in Rust. A lot of this time was spent implementing a custom
          DSL, implementing a code generation pipeline, and building a custom UI
          framework for Slack due to the large amount of views the tool needed.
          These are tasks that are more novel, and tend not to be suited to
          today’s AI tools.
        </p><p>
          However, another large chunk of time was spent writing more typical
          database queries, network calls, and the individual Slack UI
          components themselves. These are quite repetitive and simple tasks,
          and I imagine a rewrite with today’s AI tools could have saved a lot
          of time on this part.
        </p><p>
          So in my experience, most CRUD operations and pure UI views can be
          quite automateable depending on the circumstances. On a personal note,
          it seems that I would have more fulfillment working on systems that
          are more than just CRUD and UI views in that case. For instance, most
          library code I write tends to have more novel traits and requires more
          precision, so I’ve found AI tools to be way less useful there.
        </p><p>
          Though another set of cases that I’ve found vibe coding to be useful
          relates to one-off tools, prototypes, and scripts that accomplish a
          single simple task (one tool
          <a href="https://spiketime-compute-engine.lovable.app/">example</a>
          that’s visible) that supports the development of the larger system.
          Instead of spending potentially hours building an entire UI for an
          incredibly simple tool, it’s much easier to just ask Lovable to do the
          job for me so that I can get on with doing the more interesting design
          work.
        </p><p>
          Though overall, for more difficult systems the bottleneck usually
          isn’t the code, but rather the design or human element. In cases like
          these, I do think the culture tends to exaggerate how positively
          impactful AI is.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notions of Progress — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notions-of-progress</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notions-of-progress</guid>
      <pubDate>Sat, 06 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Before the eras of the Renaissance and enlightenment, there was little
          to no form of societal progress. That is, people generally died in the
          same environment they grew up in. However, the ideas of renaissance
          and enlightenment eras (eg. Freedom of Speech, Science, Democracy)
          were able to establish stable systems for incremental progress in what
          we call “developed” nations today. That is, people died in a more
          advanced (but not exponentially so) environment that they grew up in.
        </p><p>
          The last century brought us AI, personal computing, and the internet.
          These themselves were exponential leaps, similar to the printing press
          in the 15th century (which kicked off parts of the Renaissance and
          subsequent Scientific revolutions).
        </p><p>
          The point here is that we have notions of exponential progress, but we
          don’t have systems in place to drive such progress like we do for
          incremental progress.
        </p><p>
          Every year, new products will be released in various industries that
          are better than existing products on the market, but that don’t
          fundamentally change the way business is conducted for the better.
        </p><p>
          The same can’t be said for creating entirely new industries from
          scratch. For instance, ideas in computing today are largely similar to
          the ideas in computing of the 60s and 70s, just with more incremental
          progress (ie. faster hardware, C -&gt; Rust/Zig/Go, etc.). Many
          existing industries have certainly evolved with the advent of
          computing, but the fundamental ideas of those industries remain
          largely the same. Computing itself only provided an increment, though
          more like a +10 rather than a traditional +1.
        </p><p>
          I have many reasons to suspect why we don’t have a similar system for
          the exponentials, but it’s too much to write about here.
        </p><p>
          Instead, I’ll leave an observation that exponential progression leaps
          tend to come from solving “non-clear” problems (ie. Needs &gt; wants,
          non-incremental). Nearly all business settings, including startups,
          only tend to succeed when they solve “clear” problems (ie. Wants &gt;
          needs, often incremental). This skews funding towards solving “clear”
          and incremental problems instead of “non-clear” and non-incremental
          problems, which is probably why we haven’t gotten anything like Xerox
          PARC since the 70s.
        </p><p>
          With all that said, it’s not hard to see a potential reason for why we
          don’t have a system of exponential progress.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>On Democratic Creation — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#on-democratic-creation</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#on-democratic-creation</guid>
      <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          Everyone learns to write in school, but not everyone becomes an
          author. Often those who are not authors use writing for their own more
          ephemeral needs.
        </p><p>
          Anyone can pull out a piece of paper and start sketching, but not
          everyone becomes an illustrator. Often those who aren’t illustrators
          use sketching for their own more ephemeral needs.
        </p><p>
          Everyone learns basic math in school, but not everyone becomes a
          mathematician. Often those who are not mathematicians use arithmetic
          for their own more ephemeral needs.
        </p><p>
          Anyone can take pictures with a decent camera using their phone, but
          not everyone becomes a photographer. Often those who are not
          photographers take photos for their own more ephemeral (or
          authentically lasting) needs.
        </p><p>
          Anyone can build a working software system through vibe coding, but
          not everyone becomes a software engineer. Often those who are not
          software engineers use code for their own more ephemeral needs.
        </p><p>
          The idea of having amateur creators is not exclusive to AI and vibe
          coding, and in general this democratization of creation is a good
          idea. However, the quality of the creations themselves also have to be
          substantially good, and currently I don’t believe AI is doing this to
          the extent it needs to be.
        </p><p>
          Partially, this is due to the proliferation of bland chatbot
          interfaces that don’t encourage better thinking, but rather encourage
          outsourcing that thinking instead. Also partially, much of the social
          culture and media coverage that misrepresents AI to key decision
          makers is also problematic. (eg. 90% of code being AI generated does
          not indicate that anywhere even close to 90% of an engineer’s purely
          technical duties have been automated.)
        </p><p>
          Many others online seem to agree that the outsourcing is a problem.
          Unfortunately, just telling people to stop outsourcing their
          understanding isn’t going to solve this problem in a scalable manner.
          You also need to design tools that don’t encourage such outsourcing,
          but rather augment thinking instead. This will be my intention when
          desiging such tools.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Clean Code == Good UI Design (2/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#clean-code-good-ui-design-2-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#clean-code-good-ui-design-2-n-</guid>
      <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          A colleague asked me to share my thoughts on this Internet of Bugs
          video.
        </p><iframe width="560" height="315" src="https://www.youtube.com/embed/8ncQrGuunHY?si=VcPBpfs8uuoFHXiZ" title="YouTube video player" frameborder="0" allow="
            accelerometer;
            autoplay;
            clipboard-write;
            encrypted-media;
            gyroscope;
            picture-in-picture;
            web-share;
          " referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe><p><strong>The following was my response.</strong></p><p>
          There is a lot of valid information in here, especially around the
          fact of not trying to hide all the information for why a particular
          decision was made.
        </p><p>
          For me, I still treat the idea of “clean code” as a UI design problem,
          in which the code and editor are the UI for editing the system. In
          effect, that means that the editor matters just as much as the code,
          because the editor can choose which parts to show and hide. So in
          practice, a lot of our techniques for organizing code have to be based
          around how the editor shows and hides code.
        </p><p>
          However, the problem is that our modern editors are quite terrible
          when it comes to larger systems (even with agentic AI). Larger systems
          (including our last project) often contain line counts at least in the
          10s of thousands, but your editor can only show ~50 lines of code on a
          singular screen at any given point in time. In essence, modern editors
          have pinpointed their focus on writing text rather than creating
          systems.
        </p><p>
          This is why people hate the small function style presented in the
          Clean Code book. It’s solely because widespread editors make reading
          and understanding many small functions incredibly difficult due to the
          context you have to keep in your head that your editor doesn’t
          visualize.
        </p><p>For example, take this function.</p><pre><code class="lang-js">
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateReportFor</span>(<span class="hljs-params">user</span>) </span>{
  <span class="hljs-keyword">const</span> isValid = validateUser(user)
  <span class="hljs-keyword">if</span> (!isValid) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid user"</span>)

  <span class="hljs-keyword">const</span> transactions = <span class="hljs-keyword">await</span> transactionsFor(user)
  <span class="hljs-keyword">const</span> defects = <span class="hljs-keyword">await</span> defectsIn(transactions, user)
  <span class="hljs-keyword">const</span> totalParts = <span class="hljs-keyword">await</span> totalPartsFor(transactions, user)
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Report(transactions, defects, totalParts)
}
        </code></pre><p>
          Many would say this is poorly written because they would have to jump
          from <code>validateUser</code> to <code>transactionsFor</code> to
          <code>defectsIn</code> to <code>totalPartsFor</code> in their editor.
          Yet reading just this high level function shows you the outline of how
          a report is generated better than if all of the step functions were
          inlined.
        </p><p>
          The problem here is that the individual code from the step functions
          is also very important, yet modern editors will not show it alongside
          the high-level function. Due to this, it’s often considered better
          code to just inline the step functions and create 1 very large
          function instead where all the details can be seen on a single screen.
          This latter part has many of its own problems (eg. creating a tightly
          coupled mess) that often arise as time progresses.
        </p><p>
          In other words, in many cases we’re really working around poor UI
          design decisions taken by modern code editors, and pretending like the
          code is the problem. The attached images below show other aspects of
          this problem in more detail.
          <img src="./global-notes/assets/clean-code-ui-design-2.jpeg" alt="A JavaScript function called `getInterceptionPoint` which takes an argument titled `knobPoint` that does complex math and returns a 2 element array representing an x, y coordinate. The author notes that they would explain the code with a diagram, but because the code is written in a text file, such a diagram cannot be displayed.">
          <img src="./global-notes/assets/clean-code-ui-design-1.png" alt="A UI design showing how 3 sliders with no clear labels is essentially the same as calling a function with no argument labels in code.">
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Notes on Library Design — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#notes-on-library-design</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#notes-on-library-design</guid>
      <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          This probably deserves a longer piece at some point, but it’s worth
          touching up on it here briefly.
        </p><p>IMO, a good (mature) library has 2 strong design traits:</p><ol>
          <li>
            An easy to use high-level API that achieves a task with minimal
            effort.
          </li>
          <li>
            A extensive low-level API that offers so much control such that the
            higher-level API can be completely re-written from the ground up
            externally if need be.
          </li>
        </ol><p>
          Of these traits, the second is definitely the more important aspect
          for real-world/long-term use, and is my first task when creating a new
          library. The first point largely exists as a necessary consequence to
          gain adoption, or to provide an answer to the common cases. IMO, it’s
          much more of a nice to have, and can come later down the line in
          development.
        </p><p>
          In Swift Operation, I made it a priority to give you the tools to
          reconstruct the higher level API if necessary. That is, if you don’t
          like a built-in API (eg. The retry API), you should be able to
          implement your own version of it that’s tailored to your needs.
        </p><p>
          SQLiteData also did a good job at providing both higher and lower
          level control. On one hand, it exposes the
          <code>@Fetch</code> property wrapper which <code>@FetchAll</code> and
          <code>@FetchOne</code> build on top of. Additionally, it provides
          low-level tools that integrate StructuredQueries with GRDB, so you’re
          not tied to the property wrappers.
        </p><p>
          GRDB does this well too. It offers convenience APIs around
          transactions that work in 99% of scenarios, and the remaining 1% of
          cases allow you to reconstruct the way transactions work if needed.
          You can also write raw SQL alongside using its more convenient record
          request interface. StructuredQueries also does this latter part well.
        </p><p>Now for some counterexamples.</p><p>
          Tanstack Query did a good job at the higher level API, but its lower
          level could use some reconstructing. For instance, I can’t replace the
          built-in retry mechanism easily, or add composable behavior to queries
          or mutations.
        </p><p>
          Cactus did a good job providing a lower-level C FFI, but the official
          client libraries leave quite a lot to be desired. They seem to want to
          hide the complexities of model downloading, but also surface the
          low-level details of the FFI alongside those higher level details. At
          the same time, they had the library handle concurrency concerns for
          you, which may not align with your application’s desired workflow.
        </p><p>
          In Swift Cactus, I provide a higher level API for model downloading,
          but I also allow you to construct a
          <code>CactusLanguageModel</code> directly from a URL. Additionally, I
          made the language model run synchronously which gives the caller more
          control over which thread it runs on. This takes more work on the
          caller’s end to put the model behind an actor, but the synchronous
          nature also lets you put a mutex around an individual model instance
          if you want to keep thread-safe synchronous access. This later
          approach is very useful for things like generating vector embeddings
          inside a synchronous database function.
        </p><p>
          A
          <a href="https://github.com/mhayes853/swift-cactus/pull/38">higher level agentic framework</a>
          is currently in the works for Swift Cactus as I’m writing this. Here,
          you have less control over concurrency (mainly due to tool calling),
          but I think the resulting API should feel a lot easier to use once
          it’s completed. Despite all of this, the higher level agentic
          framework is built entirely on top of the existing lower level tools
          that you can use today, and you should be able to reconstruct parts of
          the agentic framework as you see fit.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>iiSU — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#iisu</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#iisu</guid>
      <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          <img src="./global-notes/assets/iisu-1.png" alt="A decorative image featuring some assets from iiSU.">
          I would link the ~20 minute presentation here, but unfortunately due
          to drama its been taken down, so you’ll get the above image instead.
        </p><p>
          This was a project shared to me by a colleague which I found
          interesting because one of my favority hobbies in 5th grade was
          creating Super Mario World ROM hacks with
          <a href="https://fusoya.eludevisibility.org/lm/">Lunar Magic</a>.
          Also, emulation was the reason that I was also able to enjoy many of
          the earlier Fire Emblem titles, and most notably Genealogy of the Holy
          War.
        </p><p>
          The main concerns I’ve read, myself included, seems to be the scope of
          the project. The former lead has an animation background, and clearly
          has an eye for aesthetics. Yet, he just announced including a social
          network, eshop, and much more (alongside the launcher) like it was no
          big deal. Since the presentation no longer exists, you can read
          <a href="https://www.androidauthority.com/iisu-presentation-features-3619732/">this</a>
          instead.
        </p><p>
          My most recent startup experience can be classified like the above
          with a somewhat similarly sized team as iiSU. In my case, we had a
          social fitness network in mind that was focused on physical events, an
          entire dynamic reflection journaling feature, and an entire literature
          narrative as an aesthetics layer (we even had drafts of chapters for
          this!). We got through rolling out the social network part, and a bit
          of the dynamic journaling part before really deciding that users
          actually wanted more of the later. Now we’re in the process of
          pivoting (new website for this will be up soon).
        </p><p>
          Regardless, it was worth it. I wouldn’t have taken on that project if
          it didn’t have a 90% chance of failure, and there were certainly
          lessons to be learned there from a business standpoint. Yet, the
          crucial thing is that if in theory the idea was executed properly, and
          received in the way we had hoped, then it could have made a
          significant impact on the way people perceived their health.
        </p><p>
          My philosophy since graduating has subsequently been to take on
          ambitious projects that have a 90% chance of failing, but if in the
          10% chance that it succeeds, then it makes a huge difference.
          <a href="https://github.com/mhayes853/swift-operation">Swift Operation</a>
          was one of those successes in my opinion, and I’ve used it extensively
          on every project I’ve undertaken since its release.
          <a href="https://github.com/mhayes853/swift-cactus">Swift Cactus</a>
          could be another in the future, it’s already gotten recognition from
          the cactus core team, and I’m currently working on making a
          <a href="https://github.com/mhayes853/swift-cactus/pull/38">higher-level framework</a>
          that makes building with local models a lot more powerful than what
          you get with FoundationModels.
        </p><p>
          Of course, those 2 projects consist of just me in my free time, so the
          scope isn’t nearly as big as my professional work. However, I also
          have other projects of my own in the background that I believe are
          even more ambitious than the 2 above. I hope to have updates on those
          soon.
        </p><p>
          AFAIK, the primary dev of iiSU’s team seems like they know
          <a href="https://www.patreon.com/cw/SapphireRhodonite">their stuff</a>, and I think it would be theoretically possible for something to
          come out of this even if it isn’t everything that was envisioned in
          the now deleted presentation. At the very least, it seems like an
          interesting project to follow even if I’m not in the target audience.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Computing Culture Origins — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#computing-culture-origins</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#computing-culture-origins</guid>
      <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          <img src="./global-notes/assets/computing-culture-origins-1.jpeg" alt="Computing is pop culture. [...] Pop culture holds a disdain for history. Pop culture is all about identity and feeling like you're participating. It has nothing to do with cooperation, the past or the future—it's living in the present. I think the same is true of most people who write code for money. They have no idea where [their culture came from]. -Alan Kay, in interview with Dr Dobb's Journal (2012)">
          Show a random CS major or Software Engineer pictures of Netwon,
          Einstein, and Feynman. Chances are they’ll recognize one of their
          pictures, typically Einstein. These people are world reknowned
          scientists.
        </p><p>
          Do the same with pictures of Dennis Ritchie, Bjarne Stroustrup, Ken
          Thompson, Brain Kernighan, and Linus Torvalds. Chances are they’ll
          recognize at least one of if not multiple of them if they’re
          interested in their craft. These people are largely responsible for
          the programming languages and operating systems they use.
        </p><p>
          Now do the same with Alan Kay, Doug Engelbart, Ivan Sutherland, and
          Ted Nelson. In the vast majority of cases that I’ve tried this, no one
          has been able to recognize even one of their pictures as well as their
          names. These people are largely responsible for the fact that they
          even have a laptop, desktop, or phone with the ability to interact in
          an online ecosystem today.
        </p><p>
          Rather unfortunately, the ideas of the last group that have largely
          been ignored, or butchered when implemented in today’s commercial
          products.
        </p><p>
          If you take modern “OOP” languages like Java, C++, Kotlin, Swift, etc.
          to be object-oriented, I recommend you really try to understand what
          Kay was getting at with the term “object-oriented” (also look at
          <a href="https://www.youtube.com/watch?v=6orsmFndx_o">Sketchpad</a> by
          Ivan Sutherland).
        </p><p>
          If you take the web to be a ubiquitous online ecosystem rich with
          discussion, convenience, and collaboration, then I recommend that you
          really look into the work of Doug Engelbart (especially
          <a href="https://www.dougengelbart.org/pubs/augment-3906.html">this</a>), <a href="https://xanadu.com/">Ted Nelson</a>, and
          <a href="https://en.wikipedia.org/wiki/ARPANET">many others</a>.
        </p><p>
          One modern sucessor to the work of these pioneers is
          <a href="https://worrydream.com/">Bret Victor</a> and
          <a href="https://dynamicland.org/">Dynamicland</a> (which is very
          anti-Vision Pro). In fact, you can find archives of the
          <a href="https://worrydream.com/refs/">work</a> of many of the above
          pioneers on his
          <a href="https://worrydream.com/Links2025/">website</a>.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>“Surveillance Driven Development” — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#-surveillance-driven-development-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#-surveillance-driven-development-</guid>
      <pubDate>Sat, 22 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          <img src="./global-notes/assets/surveillance-driven-development-1.jpeg" alt="As a thought experiment, try replacing the word data with surveillance, and observe if common phrases still sound so good [93]. How about this: 'In our surveillance-driven organization we collect real-time surveillance streams and store them in our surveillance warehouse. Our surveillance scientists use advanced analytics and surveillance processing in order to derive new insights.'">
          This is one of the problems of the web and mass centralization. By its
          very design, all remote data is centralized, and this design often
          encourages such surveillance like behavior.
        </p><p>
          If anything, reading
          <em>Designing Data Intensive Applications</em> (source of the quote)
          has taught me that large centralized distributed systems that make
          high-stakes decisions for people are terrible ideas. From the
          technical standpoint, often the best state a large system can be in is
          “eventually consistent”. That is, a state in which not all necessary
          information (much of which is completely invisible to the end-user) is
          guaranteed to be present to make a proper decision at any given
          moment.
        </p><p>
          This isn’t even mentioning the fact that as system designers we are
          often making systemic decisions in contexts that don’t reflect the
          actual context in which the system operates in.
        </p><p>
          My take on this is that data and decision making power are best kept
          by the individual, and not the organization. Rather, it should be the
          job of the organization to enhance the decision making power of the
          individual (eg. Public education teaches us to read, and reading helps
          us make better decisions).
        </p><p>
          This is particularly why I’m interested in heavy client-side based
          software solutions in today’s landscape (eg. Native mobile apps, local
          LLMs) rather than remote/web based solutions. I try to limit the
          server side component as much as possible on small/solo projects.
          Often I find that it isn’t necessary to create a dedicated backend in
          the first place for many useful products outside of proxying requests
          to third parties.
        </p><p>
          Of course, long term I’m much more interested in tomorrow’s landscape,
          which ideally will embrace the idea of individual creative freedom far
          more than its predecessors. That is, I would rather we treat the
          masses as capable creators rather than “the audience”. The web and
          subsequent AI-driven culture fails horrifically at this.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Why not Social Media? — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#why-not-social-media-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#why-not-social-media-</guid>
      <pubDate>Fri, 21 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I’m often asked why I’m not hyper active on platforms like X, Threads,
          or Bluesky, and why I’m opting for this global notes style thing
          instead.
        </p><p>
          The simple answer to this is that all the mainstream social media
          platforms are not designed for real creative expression. They’ve
          adopted the “easy to use, perpetual beginner” mindset, and have
          amplified it across billions of users. This is quite disastrous in my
          opinion.
        </p><p>
          On this website, I can use whatever HTML, CSS, and JavaScript I want
          to express my work. I can even embed entire
          <a href="https://whypeople.xyz/liquid-glass">interactable programs</a>
          directly into my writing. (I wish to do this more in the future). On
          social media, you’re essentially limited to plain text, video, and
          photos, which is very rigid in comparison, and this is not even taking
          the “algorithm” into account.
        </p><p>
          I am very fortunate to have had the natural interest in technology and
          software, as well as the natural ability to understand the complex
          abstact concepts that have enabled me to unlock this kind of
          expression in my work. This is not most of the world, and it’s quite
          saddening to see that they get much more limited forms of expression.
        </p><p>
          Text on a black background, simple photos, and static videos delivered
          to a one-size-fits-all audience and displayed in a 6-inch rectangle
          are not powerful enough mediums to communicate complex ideas that
          determine the direction of society. Much of these ideas rely on trends
          in large complex datasets, or disastrous things we cannot see (eg. The
          climate problem). From a UI design standpoint, static content isn’t
          enough to convey everything that’s needed with this complexity.
        </p><p>
          Additionally, centralized large-scale algorithms that make the
          decisions on what media to surface are also not ideal when those
          decisions are made based on impulsive trends. Nearly all influential
          media in the world (eg. The US Constitution, “Common Sense”) did not
          use extensive emotional/moral baiting rhetoric to convey their ideas
          in the way we see on social media today. Thomas Paine didn’t need to
          participate in the “attention economy” in writing “Common Sense”,
          which was one of the influential documents in the wake of the American
          Revolution.
        </p><p>
          It’s true that my “visibility” on this site is far lower than if I
          were more active on social media, but my intention is only merely to
          reach an audience with the capable creative abilities to seek
          something greater. If you’re reading this of your own accord, there’s
          a high chance that you have such ability, and you’re exactly the type
          of person that I’m trying to reach.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>CleanMyMac + Xcode — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#cleanmymac-xcode</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#cleanmymac-xcode</guid>
      <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          CleanMyMac is software meant for cleaning up junk files on your mac
          when your disk inevitably fills up with Xcode’s shenanigans.
          Incredibly, CleanMyMac will refuse to launch when your disk space is
          actually full!
        </p><p>
          Now the real question is why does Xcode need to take up so much space?
          Even more importantly, why does Xcode go to such lengths to hide the
          actual contents of the things it stores? This much invisibility is not
          very nice…
        </p><p>
          Caches = storing information that allows us to access information…
        </p><p>
          The interesting thing is that the idea of disk-based storage forces us
          to think in very abstract terms since you can’t visually see what’s
          being stored, which most human minds massively fail at. MacOS also
          likes to put a “user-friendly facade” around the whole thing, because
          a lot of that storage is taken by various caches and internal
          application data. This facade comes with the tradeoff that the larger
          part of society is completely oblivious as to what their machines are
          actually doing.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Clean Code == Good UI Design (1/N) — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#clean-code-good-ui-design-1-n-</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#clean-code-good-ui-design-1-n-</guid>
      <pubDate>Wed, 19 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          I tend to think of writing clean code as good UI design (this is
          something I want to write about extensively at some point).
          Unfortunately, modern text editors and programming languages don’t see
          things this way (this is also something I want to write about
          extensively at some point), and I often find myself enjoying fun
          illustrations like such.
          <img src="./global-notes/assets/clean-code-ui-design-1.png" alt="A UI design showing how 3 sliders with no clear labels is essentially the same as calling a function with no argument labels in code.">
          <a href="https://worrydream.com/LearnableProgramming/">Source</a>
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Initial TCA 2.0 Thoughts — Global Notes</title>
      <link>https://whypeople.xyz/global-notes#initial-tca-2-0-thoughts</link>
      <guid isPermaLink="true">https://whypeople.xyz/global-notes#initial-tca-2-0-thoughts</guid>
      <pubDate>Wed, 19 Nov 2025 00:00:00 GMT</pubDate>
      <description>Ideas in my head that I don&apos;t have time to formally write about, and that are not easily expressible on social media.</description>
      <content:encoded><![CDATA[<p>
          This looks interesting, as someone who’s casually used TCA since
          pre-Reducer protocol days, I can give some thoughts here.
        </p><p>
          I like how the ping-ponging of actions has been taken away, this was
          extremely annoying, and I generally gated all of these ping-ponging
          actions inside an <code>Effect</code> enum. Eg.
        </p><pre><code class="lang-swift">
<span class="hljs-comment">// This</span>

@Reducer
<span class="hljs-keyword">struct</span> Feature {
  <span class="hljs-comment">// ...</span>

  <span class="hljs-keyword">enum</span> Action {
    <span class="hljs-function"><span class="hljs-keyword">case</span> buttonTapped
    <span class="hljs-keyword">case</span> <span class="hljs-title">effect</span>(<span class="hljs-params">Effect</span>)

    <span class="hljs-keyword">enum</span> Effect </span>{
      <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">dataLoaded</span>(<span class="hljs-params">Result&lt;SomeData, any Error&gt;</span>)
    }
  }

  <span class="hljs-keyword">var</span> body: some ReducerOf&lt;Self&gt; </span>{
    Reduce { state, action <span class="hljs-keyword">in</span>
      <span class="hljs-keyword">switch</span> action {
      <span class="hljs-keyword">case</span> .buttonTapped:
        <span class="hljs-keyword">return</span> .run { send <span class="hljs-keyword">in</span>
          <span class="hljs-keyword">let</span> result = <span class="hljs-keyword">await</span> Result { <span class="hljs-function"><span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> <span class="hljs-title">someWork</span>(<span class="hljs-params"></span>) }
          <span class="hljs-keyword">await</span> <span class="hljs-title">send</span>(<span class="hljs-params">.effect(.dataLoaded(result</span>))
        }
      <span class="hljs-keyword">case</span> .<span class="hljs-title">effect</span>(<span class="hljs-params">.dataLoaded(<span class="hljs-keyword">let</span> result</span>)):
        <span class="hljs-comment">// ...</span>
      }
    }
  }
}

<span class="hljs-comment">// Now Becomes This</span>

@Reducer
<span class="hljs-keyword">struct</span> Feature </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-keyword">enum</span> Action {
    <span class="hljs-keyword">case</span> buttonTapped
  }

  <span class="hljs-keyword">var</span> body: some ReducerOf&lt;Self&gt; {
    Reduce { state, action <span class="hljs-keyword">in</span>
      <span class="hljs-keyword">switch</span> action {
      <span class="hljs-keyword">case</span> .buttonTapped:
        <span class="hljs-keyword">return</span> .run { store <span class="hljs-keyword">in</span>
          <span class="hljs-keyword">let</span> result = <span class="hljs-keyword">await</span> Result { <span class="hljs-function"><span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> <span class="hljs-title">someWork</span>(<span class="hljs-params"></span>) }
          <span class="hljs-keyword">try</span> store.modify </span>{ <span class="hljs-comment">/* Just set state in here */</span> }
        }
      }
    }
  }
}
        </code></pre><p>
          With the old way of doing things, it was quite easy to lose focus of
          the overall control flow.
        </p><p>
          In terms of <code>Store</code> vs <code>StoreActor</code>, I would
          rather that they also have a non-Sendable store type, and simply wrap
          the actor isolation on top. This is what I did with
          <code>CactusLanguageModel</code> in
          <a href="https://github.com/mhayes853/swift-cactus">Swift Cactus</a>,
          and the flexibility is quite nice. I can choose to call the language
          model synchronously in a thread-safe manner using <code>Mutex</code>,
          or asynchronously by wrapping it in an actor. I think it should be the
          same for the store as well.
        </p><p>
          <code>onMount</code> and <code>onDismount</code> are also healthy
          additions, especially since they’re not tied to any one view system
          (which I presume is necessary for the cross platform support they want
          to achieve). Long ago, in one of my first apps, I remeber defining the
          notion of an <code>AppearableAction</code> which essentially tried to
          automate the whole <code>onAppear</code> and
          <code>onDisappear</code> dance. Suffice to say,
          <code>onMount</code> and <code>onDismount</code> are better than those
          tools.
        </p><p>
          The new <code>onChange</code> behavior is also very welcome, and it’s
          definitely more intuitive.
        </p><p>
          I also presume the removal of <code>BindingReducer</code> is a natural
          consequence of wanting to make things cross platform.
        </p><p>
          I like the overall direction of turning features into descriptions
          rather than imperative messes. Swift itself is still quite imperative
          though which is admittedly annoying.
        </p><br>]]></content:encoded>
    </item>
    <item>
      <title>Approach to &apos;Work Ethic&apos;</title>
      <link>https://whypeople.xyz/approach-to-work-ethic</link>
      <guid isPermaLink="true">https://whypeople.xyz/approach-to-work-ethic</guid>
      <pubDate>Sat, 15 Nov 2025 00:00:00 GMT</pubDate>
      <description>A different view.</description>
      <content:encoded><![CDATA[<h1 id="approach-to-work-ethic-" class="title">
          Approach to “Work Ethic”
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p><strong>Visions</strong> - not goals, not plans</p>
        <p><strong>Just Doing</strong> - not consistency, not discipline</p>
        <p><strong>Showcases</strong> - not deadlines, not accountability</p>
        <p><strong>Meaning</strong> - not numbers, not just results</p>
        <p>
          <strong>Great Ideas</strong> - not just execution, not just
          implementation
        </p>
        <p>
          <strong>Deep Thinking</strong> - not just action, not quick reactions
        </p>
        <p><strong>Equal Collaboration</strong> - not juniors, not seniors</p>
        <p>
          Though such “nots” may be imposed on me, and it will probably look
          like I’m adhering to them, they are the enemies of great work if I am
          chained to them internally.
        </p>
        <blockquote>
          <p>
            Giving a professional illustrator a goal for a poster usually
            results in what was desired. If one tries this with an artist, one
            will get what the artist needed to create that day. Sometimes we
            make, to have, sometimes to know and express. The pursuit of Art
            always sets off plans and goals, but plans and goals don't
            always give rise to Art. If "visions not goals" opens the
            heavens, it is important to find artistic people to conceive the
            projects.
          </p>
          <p>
            Thus the "people not projects" principle was the other
            cornerstone of ARPA/PARC’s success. Because of the normal
            distribution of talents and drive in the world, a depressingly large
            percentage of organizational processes have been designed to deal
            with people of moderate ability, motivation, and trust. We can
            easily see this in most walks of life today, but also astoundingly
            in corporate, university, and government research. ARPA/PARC had two
            main thresholds: self-motivation and ability. They cultivated people
            who "had to do, paid or not" and "whose doings were
            likely to be highly interesting and important". Thus
            conventional oversight was not only not needed, but was not really
            possible. "Peer review" wasn't easily done even with
            actual peers.
          </p>
          <p>
            The situation was "out of control", yet extremely
            productive and not at all anarchic. "Out of control"
            because artists have to do what they have to do. "Extremely
            productive" because a great vision acts like a magnetic field
            from the future that aligns all the little iron particle artists to
            point to “North” without having to see it. They then make their own
            paths to the future. Xerox often was shocked at the PARC process and
            declared it out of control, but they didn't understand that the
            context was so powerful and compelling and the good will so
            abundant, that the artists worked happily at their version of the
            vision. The results were an enormous collection of breakthroughs,
            some of which we are celebrating today.
          </p>
          <p>
            — Alan Kay
            <a href="https://tinlizzie.org/VPRIPapers/m2004001_power.pdf">(The Power of Context)</a>
          </p>
        </blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Protocols and Possibility</title>
      <link>https://whypeople.xyz/protocols-and-possibility</link>
      <guid isPermaLink="true">https://whypeople.xyz/protocols-and-possibility</guid>
      <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
      <description>Protocol size determines possibility, and possibilities are a good thing.</description>
      <content:encoded><![CDATA[<h1 id="protocols-and-possibility" class="title">
          Protocols and Possibility
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          I recently dove into looking at Bitwarden’s iOS codebase for fun, and
          to find resources for other engineers that I work with to learn native
          the native mobile languages. Since Bitwarden is quite large, they
          actually have to take architecture seriously from a code readability,
          testability, and maintainability standpoint, and not leave it off as a
          nice to have.
        </p>
        <p>Regardless, I saw a lot of massive protocols like this.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">AuthService</span> </span>{
    <span class="hljs-keyword">var</span> callbackUrlScheme: <span class="hljs-type">String</span> { <span class="hljs-keyword">get</span> }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">answerLoginRequest</span><span class="hljs-params">(<span class="hljs-number">_</span> request: LoginRequest, approve: Bool)</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">checkPendingLoginRequest</span><span class="hljs-params">(withId id: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">LoginRequest</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">denyAllLoginRequests</span><span class="hljs-params">(<span class="hljs-number">_</span> requests: [LoginRequest])</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generateSingleSignOnUrl</span><span class="hljs-params">(from organizationIdentifier: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; (url: <span class="hljs-type">URL</span>, state: <span class="hljs-type">String</span>)

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getPendingAdminLoginRequest</span><span class="hljs-params">(userId: String?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">PendingAdminLoginRequest</span>?

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getPendingLoginRequest</span><span class="hljs-params">(withId id: String?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">LoginRequest</span>]

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">hashPassword</span><span class="hljs-params">(password: String, purpose: HashPurpose)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initiateLoginWithDevice</span><span class="hljs-params">(
        email: String,
        type: AuthRequestType
    )</span></span> async <span class="hljs-keyword">throws</span> -&gt; (authRequestResponse: <span class="hljs-type">AuthRequestResponse</span>, requestId: <span class="hljs-type">String</span>)

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loginWithDevice</span><span class="hljs-params">(
        <span class="hljs-number">_</span> loginRequest: LoginRequest,
        email: String,
        isAuthenticated: Bool,
        captchaToken: String?
    )</span></span> async <span class="hljs-keyword">throws</span> -&gt; (<span class="hljs-type">String</span>, <span class="hljs-type">String</span>)

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loginWithMasterPassword</span><span class="hljs-params">(
        <span class="hljs-number">_</span> password: String,
        username: String,
        captchaToken: String?,
        isNewAccount: Bool
    )</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loginWithSingleSignOn</span><span class="hljs-params">(code: String, email: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">LoginUnlockMethod</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loginWithTwoFactorCode</span><span class="hljs-params">(
        email: String,
        code: String,
        method: TwoFactorAuthMethod,
        remember: Bool,
        captchaToken: String?
    )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">LoginUnlockMethod</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">requirePasswordChange</span><span class="hljs-params">(
        email: String,
        isPreAuth: Bool,
        masterPassword: String,
        policy: MasterPasswordPolicyOptions?
    )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Bool</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">resendVerificationCodeEmail</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">resendNewDeviceOtp</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setPendingAdminLoginRequest</span><span class="hljs-params">(<span class="hljs-number">_</span> adminLoginRequest: PendingAdminLoginRequest?, userId: String?)</span></span> async <span class="hljs-keyword">throws</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">webAuthenticationSession</span><span class="hljs-params">(
        url: URL,
        completionHandler: @escaping ASWebAuthenticationSession.CompletionHandler
    )</span></span> -&gt; <span class="hljs-type">ASWebAuthenticationSession</span>
}
        </code></pre>
        <p>This protocol has 18 requirements, which is quite a lot.</p>
        <p>
          However, I’ve also seen such large protocols parroted around as
          general advice, though not explicitly. No one is literally saying,
          “You should make protocols as large as possible!”, but you probably
          have heard of something called the Repository pattern. An average
          Repository protocol looks like this.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NotesRepository</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNote</span><span class="hljs-params">(by id: UUID)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Note</span>?
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">allNotes</span><span class="hljs-params">(<span class="hljs-keyword">for</span> notebookId: UUID?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNotes</span><span class="hljs-params">(by text: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">moveNote</span><span class="hljs-params">(from srcNotebookId: UUID, to dstNotebookId: UUID)</span></span> async <span class="hljs-keyword">throws</span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with ids: Set&lt;UUID&gt;)</span></span> async <span class="hljs-keyword">throws</span>
}
        </code></pre>
        <p>
          Even still, 6 protocol requirements is quite a lot. Most protocols
          that you conform to from most libraries and frameworks, including
          Apple’s, do not have nearly as many requirements. Or, if those
          protocols do have lots of requirements, it’s usually an some kind of
          “delegate” protocol where most of the requirements are optional, and
          you only end up implementing a small subset of the requirements most
          of the time.
        </p>
        <p>
          I would rather have something like this instead of
          <code>NotesRepository</code>. Where each protocol defines a small
          class of cohesive functionality.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span>
}

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteMover</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">moveNote</span><span class="hljs-params">(from srcNotebookId: UUID, to dstNotebookId: UUID)</span></span> async <span class="hljs-keyword">throws</span>
}

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteDeleter</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with ids: Set&lt;UUID&gt;)</span></span> async <span class="hljs-keyword">throws</span>
}

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteFinder</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNote</span><span class="hljs-params">(by id: UUID)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Note</span>?
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">allNotes</span><span class="hljs-params">(<span class="hljs-keyword">for</span> notebookId: UUID?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNotes</span><span class="hljs-params">(by text: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]
}
        </code></pre>
        <p>
          Inevitably, this will turn into a piece about what an optimal protocol
          size is. Naturally, my answer is of course that “it depends”, but it’s
          usually not the kind of large protocols that you more commonly see in
          the wild. Rather, it’s about leaving open as many different
          conformance possibilities as possible, and large protocols with many
          requirements tend to not provide many conformance possibilities.
        </p>
        <br>
        <h2 id="the-case-against-large-protocols">
          The Case Against Large Protocols
        </h2>
        <p>
          Before jumping in, I don’t want to give the impression that such
          protocols are always bad. Large protocols are fine if the context they
          are used in has an answer for the generalized case that I will bring
          against them.
        </p>
        <p>
          There are 2 primary pain points that I have with large protocols.
          First, is the fact that often no parts of your app need to depend on
          the entire protocol, but rather only a few requirements of it at a
          time. Second, larger protocols are harder to conform to in general,
          and are harder to compose (ie. Creating large pieces of functionality
          by composing smaller conformance’s together).
        </p>
        <p>
          The main point that you should take away from this is that smaller and
          easier it is to conform to a protocol, you obtain more possibilities
          for implementing that protocol. This in turn gives you more options
          when you need to add or modify a feature in your code.
        </p>
        <h3 id="unnecessary-dependence">Unnecessary Dependence</h3>
        <p>
          The functionality that often depends on large protocols often only
          needs a small subset of the requirements (typically only 1-3 in 90% of
          cases).
        </p>
        <p>
          For instance, this could be a pure SwiftUI view, view model, or TCA
          reducer for a UI component only needs to depend on functionality that
          performs the data actions taken by the user. This could be something
          like adding a new note an HTTP API when a new note is created through
          a form. In this case, such a pure view, view model, or reducer only
          needs to depend on this protocol.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span>
}
        </code></pre>
        <p>Yet instead, we often end up depending on this.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NotesRepository</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNote</span><span class="hljs-params">(by id: Int)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Note</span>?
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">allNotes</span><span class="hljs-params">(<span class="hljs-keyword">for</span> notebookId: Int?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNotes</span><span class="hljs-params">(by text: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>]

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">moveNote</span><span class="hljs-params">(from srcNotebookId: Int, to dstNotebookId: Int)</span></span> async <span class="hljs-keyword">throws</span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with ids: Set&lt;Int&gt;)</span></span> async <span class="hljs-keyword">throws</span>
}
        </code></pre>
        <p>
          If we’re using Xcode previews, it would be wise to be able to preview
          what our UI is like when the save operation invoked by a note creation
          form fails. This allows us to experience the user’s pain as they
          try-try-try again to use our broken app. We’ll likely rely on a mock
          to achieve this, because this makes it easy to put the UI in a failing
          state.
        </p>
        <p>
          Since our <code>NoteSaver</code> protocol only has a single
          requirement, creating a mock is quite easy. In fact, we can just
          create it directly in the preview block because it’s so easy. We can
          even throw in some fake delay, just to simulate the mindless wait that
          our frustrated user will experience for real.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AddNoteForm</span>: <span class="hljs-title">View</span> </span>{
  <span class="hljs-keyword">init</span>(saver: any <span class="hljs-type">NoteSaver</span>) {
    <span class="hljs-comment">// ...</span>
  }
  <span class="hljs-comment">// ...</span>
}

#<span class="hljs-type">Preview</span> {
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FailingSaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
      <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}

      <span class="hljs-keyword">try</span> await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-number">3</span>))
      <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>()
    }
  }

  <span class="hljs-type">AddNoteForm</span>(saver: <span class="hljs-type">FailingSaver</span>())
}
        </code></pre>
        <p>
          Ok… Now let’s decrease the delay to see what happens to a user with a
          higher speed connection.
        </p>
        <pre><code class="lang-diff">
#<span class="hljs-type">Preview</span> {
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FailingSaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
      <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}

-      <span class="hljs-keyword">try</span> await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-number">3</span>))
+      <span class="hljs-keyword">try</span> await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-number">1</span>))
      <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>()
    }
  }

  <span class="hljs-type">AddNoteForm</span>(saver: <span class="hljs-type">FailingSaver</span>())
}
        </code></pre>
        <p>
          I can easily do this because our <code>FailingSaver</code> is quite
          small and is easy to change in isolation without affecting other
          views.
        </p>
        <p>
          Now instead of depending on small protocol, let’s try previewing
          <code>AddNoteForm</code> this while depending on our larger
          <code>NoteRepository</code> protocol.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AddNoteForm</span>: <span class="hljs-title">View</span> </span>{
  <span class="hljs-keyword">init</span>(saver: any <span class="hljs-type">NoteRepository</span>) {
    <span class="hljs-comment">// ...</span>
  }
  <span class="hljs-comment">// ...</span>
}

#<span class="hljs-type">Preview</span> {
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FailingSaver</span>: <span class="hljs-title">NoteRepository</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNote</span><span class="hljs-params">(by id: Int)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Note</span>? { <span class="hljs-literal">nil</span> }
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">allNotes</span><span class="hljs-params">(<span class="hljs-keyword">for</span> notebookId: Int?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>] { [] }
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNotes</span><span class="hljs-params">(by text: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>] { [] }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
      <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}

      <span class="hljs-keyword">try</span> await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-number">3</span>))
      <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>()
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">moveNote</span><span class="hljs-params">(from srcNotebookId: Int, to dstNotebookId: Int)</span></span> async <span class="hljs-keyword">throws</span> {}
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with ids: Set&lt;Int&gt;)</span></span> async <span class="hljs-keyword">throws</span> {}
  }

  <span class="hljs-type">AddNoteForm</span>(saver: <span class="hljs-type">FailingSaver</span>())
}
        </code></pre>
        <p>
          There’s just so many extra requirements to implement here. Having a
          bunch of adhoc conformances to <code>NoteRepository</code> would be
          quite annoying. Often, most apps will just create a single mock, and
          reuse it everywhere because creating many conformance’s is quite
          annoying. This mock will need to get large to accommodate artificial
          failure modes.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// In some other file...</span>

<span class="hljs-comment">// This is the only mock conformance to NoteRepository in the app.</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MockNotesRepository</span>: <span class="hljs-title">NoteRepository</span> </span>{
  <span class="hljs-keyword">var</span> notes = [<span class="hljs-type">Int</span>?: [<span class="hljs-type">Note</span>]]()
  <span class="hljs-keyword">var</span> savedNotes = [<span class="hljs-type">Note</span>]()
  <span class="hljs-keyword">var</span> saveError: (any <span class="hljs-type">Error</span>)?
  <span class="hljs-keyword">var</span> moveError: (any <span class="hljs-type">Error</span>)?
  <span class="hljs-keyword">var</span> deleteError: (any <span class="hljs-type">Error</span>)?

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNote</span><span class="hljs-params">(by id: Int)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Note</span>? {
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">allNotes</span><span class="hljs-params">(<span class="hljs-keyword">for</span> notebookId: Int?)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>] {
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">findNotes</span><span class="hljs-params">(by text: String)</span></span> async <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Note</span>] {
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">moveNote</span><span class="hljs-params">(from srcNotebookId: Int, to dstNotebookId: Int)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with ids: Set&lt;Int&gt;)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <p>
          Now, if we want to add our artificial delay to saving a note, we have
          to update the global mock that’s used by all previews. Others may want
          to do the same, and this leads to contention, and bloating the mock
          with all sorts of weird functionality.
        </p>
        <p>
          If we’re like Bitwarden, or we don’t want to end up with code that
          regresses constantly, we’ll also likely have a test suite (though this
          may be considered optional for some code bases). It turns out that
          being able to spin up adhoc mocks is also quite useful in those cases.
          For instance, if we drive <code>AddNoteForm</code> with an Observable
          model, we may end up with this.
        </p>
        <pre><code class="lang-swift">
@<span class="hljs-type">Observable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddNoteFormModel</span> </span>{
  <span class="hljs-keyword">init</span>(saver: any <span class="hljs-type">NoteSaver</span>) {
    <span class="hljs-comment">// ...</span>
  }
  <span class="hljs-comment">// ...</span>
}

@<span class="hljs-type">Test</span>(<span class="hljs-string">"Shows Alert When Failing To Add Note"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">showsAlertWhenFailingToAddNote</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FailingSaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
      <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}
      <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>()
    }
  }

  <span class="hljs-keyword">let</span> model = <span class="hljs-type">AddNoteFormModel</span>(saver: <span class="hljs-type">FailingSaver</span>())
  model.title = <span class="hljs-string">"My note"</span>
  model.contents = <span class="hljs-string">"This is fun"</span>

  #expect(model.alert == <span class="hljs-literal">nil</span>)
  await model.submitted()
  #expect(model.alert == .failedToSaveNote)
}
        </code></pre>
        <p>
          If we had made <code>AddNoteFormModel</code> depend on
          <code>NotesRepository</code>, this process would again be cumbersome,
          and we would likely have to resort to using our one and only
          <code>MockNotesRepository</code>.
        </p>
        <p>
          Now, I don’t think reusing a mock conformance is a bad idea. In fact,
          it’s a great idea! However, a mock that’s reused everywhere also
          cannot always have its behavior adjusted for a one-off experiments,
          previews, or test cases. Continuously adjusting a shared mock in these
          ways would lead to the shared mock becoming large, complicated, and
          harder to change over time.
        </p>
        <h3 id="difficulty-of-conformance">Difficulty of Conformance</h3>
        <p>
          Think of protocols as a set of possibilities. That is, a protocol
          defines a subset of types that conform to it. The
          <code>Equatable</code> protocol defines the subset of types that have
          an equality mechanism, and likewise SwiftUI’s
          <code>View</code> protocol defines the subset of types that can be
          rendered in a user interface. The harder it is to conform to the
          protocol, the smaller the subset is. Each type in the subset
          represents a different possibility of how its requirements are carried
          out.
        </p>
        <p>
          In other words, protocols that are easy to conform to have many
          possibilities which means that you have <em>many options</em> for
          conforming to them. On the flip side, a protocol that is harder to
          conform to limits the number of options you have for making a
          conformance.
        </p>
        <p>
          It goes without saying that smaller protocols are often easier to
          conform to, and therefore provide more possibilities.
        </p>
        <p>
          With smaller protocols, you’re more likely to add new behavior to your
          app by creating a new conformance. With larger protocols, you’re more
          likely to make changes to the fewer existing conforming types you have
          directly in order to add new behavior.
        </p>
        <h4 id="feed-filters-dsl">Feed Filters DSL</h4>
        <p>
          Here’s a small protocol that we’ll call <code>FeedFilter</code>. A
          <code>FeedFilter</code> is a mechanism to filter out a specific
          content type from a typical social media feed. Note that in this
          example, there are multiple different content types, hence the
          <code>Content</code> associated type.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">FeedFilter</span>&lt;<span class="hljs-title">Content</span>&gt; </span>{
  associatedtype <span class="hljs-type">Content</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: Content)</span></span> async -&gt; <span class="hljs-type">Bool</span>
}
        </code></pre>
        <p>
          <code>FeedFilter</code> is an incredibly easy protocol to conform to,
          so let’s make our first conformance!
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ThreadsExcludeIfControversialFilter</span>: <span class="hljs-title">FeedFilter</span> </span>{
  <span class="hljs-keyword">let</span> preferences: <span class="hljs-type">Preferences</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: Thread)</span></span> -&gt; <span class="hljs-type">Bool</span> {
    <span class="hljs-keyword">self</span>.preferences.threadFilterControvesyRate &lt; content.downvotePercentage
  }
}
        </code></pre>
        <p>That was easy! Let’s make another!</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ThreadsExcludeIfDownvotedFilter</span>: <span class="hljs-title">FeedFilter</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: Thread)</span></span> -&gt; <span class="hljs-type">Bool</span> {
    content.userVote != .downvote
  }
}
        </code></pre>
        <p>
          Now, what if we want to create a filter that excludes thread if either
          the user downvoted them, or if the thread if controversial? We can
          certainly make a
          <code>ThreadsExcludeIfDownvotedOrControversialFilter</code>, but now
          we’re getting into
          <code>AbstractSingletonProxyFactoryBean</code> territory. All we’re
          trying to do either is run 1 filter before the other, so let’s make a
          simple conformance to do just that.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">FeedFilter</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    before other: some FeedFilter&lt;Content&gt;
  )</span></span> -&gt; some <span class="hljs-type">FeedFilter</span>&lt;<span class="hljs-type">Content</span>&gt; {
    <span class="hljs-type">RunBeforeFilter</span>(a: <span class="hljs-keyword">self</span>, b: other)
  }
}

<span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">RunBeforeFilter</span>&lt;
  <span class="hljs-title">A</span>: <span class="hljs-title">FeedFilter</span>,
  <span class="hljs-title">B</span>: <span class="hljs-title">FeedFilter</span>
&gt;: <span class="hljs-title">FeedFilter</span> <span class="hljs-title">where</span> <span class="hljs-title">A</span>.<span class="hljs-title">Content</span> == <span class="hljs-title">B</span>.<span class="hljs-title">Content</span> </span>{
  <span class="hljs-keyword">let</span> a: <span class="hljs-type">A</span>
  <span class="hljs-keyword">let</span> b: <span class="hljs-type">B</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: A.Content)</span></span> async -&gt; <span class="hljs-type">Bool</span> {
    <span class="hljs-keyword">guard</span> await <span class="hljs-keyword">self</span>.a.shouldIncludeInFeed(content: content) <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
    <span class="hljs-keyword">return</span> await <span class="hljs-keyword">self</span>.b.shouldIncludeInFeed(content: content)
  }
}
        </code></pre>
        <p>
          Now we can easily create our
          <code>downvotedOrControvesialFilter</code> like so.
        </p>
        <pre><code class="lang-swift">
let downvotedOrControversial = ThreadsExcludeIfControversialFilter()
  .run(<span class="hljs-keyword">before</span>: <span class="hljs-title">ThreadsExcludeIfDownvotedFilter</span>())
        </code></pre>
        <p>
          In fact <code>RunBeforeFilter</code> works for any content type, and
          any 2 filters with the same content type. Now let’s try making another
          conformance that also doesn’t care too much about the content type.
          For instance, lots of scrollable content on social media contains some
          very forbidden words. Let’s make a filter to filter those words.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">HiddenPhrasesFilterable</span> </span>{
  <span class="hljs-keyword">var</span> language: <span class="hljs-type">Language</span> { <span class="hljs-keyword">get</span> }
  <span class="hljs-keyword">var</span> textBlocks: [(<span class="hljs-type">String</span>, <span class="hljs-type">HiddenPhraseOptions</span>)] { <span class="hljs-keyword">get</span> }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">HiddenPhrasesFilter</span>&lt;<span class="hljs-title">Content</span>: <span class="hljs-title">HiddenPhrasesFilterable</span>&gt;: <span class="hljs-title">FeedFilter</span> </span>{
  <span class="hljs-keyword">let</span> phrases: <span class="hljs-type">HiddenPhrases</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: Content)</span></span> -&gt; <span class="hljs-type">Bool</span> {
    <span class="hljs-comment">// Tokenize the content by word with NLTokenizer and use a trie</span>
    <span class="hljs-comment">// to efficiently match phrases in HiddenPhrases...</span>
  }
}
        </code></pre>
        <p>
          I’ve omitted the implementation of
          <code>HiddenPhrasesFilter</code> for brevity, but suffice to say it
          used the algorithm described by the comment above. However, the main
          point was that we now have a filter that works on any kind of content
          that conforms to <code>HiddenPhrasesFilterable</code>.
        </p>
        <p>
          Finally, let’s make a simple conformance that works with
          <code>Comment</code>s instead of just <code>Thread</code>s.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CommentExcludeIfNoRepliesFilter</span>: <span class="hljs-title">FeedFilter</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">shouldIncludeInFeed</span><span class="hljs-params">(content: Comment)</span></span> -&gt; <span class="hljs-type">Bool</span> {
    content.replyCount &gt; <span class="hljs-number">0</span>
  }
}
        </code></pre>
        <p>
          We can keep going, but the gist of this is that we can reuse filtering
          logic to create separate content filtering pipelines. I ended up with
          something like this.
        </p>
        <pre><code class="lang-swift">
extension FeedFilter where Content == <span class="hljs-class">Thread </span>{
  static func defaultThreads(
<span class="hljs-symbol">    preferences:</span> Preferences,
<span class="hljs-symbol">    accountsActor:</span> some SavedAccountsActor
  ) -&gt; some FeedFilter<span class="hljs-params">&lt;Content&gt;</span> {
    NSFWFilter(preferences: preferences)
      .run(
<span class="hljs-symbol">        before:</span> ThreadsExcludeIfUserDownvotedFilter()
          .<span class="hljs-class">onlyRunIf </span>{ preferences.shouldFilterDownvotedThreads }
      )
      .run(
<span class="hljs-symbol">        before:</span> ThreadsExcludeIfControversialFilter(preferences: preferences)
          .<span class="hljs-class">onlyRunIf </span>{ preferences.shouldFilterControversialThreads }
      )
      .run(
<span class="hljs-symbol">        before:</span> ContentMarkerFilter(
<span class="hljs-symbol">          accountsActor:</span> accountsActor,
<span class="hljs-symbol">          contentMarker:</span> .markedAsReadThreads
        )
        .<span class="hljs-class">onlyRunIf </span>{ preferences.shouldFilterThreadsMarkedAsRead }
      )
      .run(
<span class="hljs-symbol">        before:</span> ContentMarkerFilter(
<span class="hljs-symbol">          accountsActor:</span> accountsActor,
<span class="hljs-symbol">          contentMarker:</span> .hiddenThreads
        )
      )
      .run(before: HiddenPhrasesFilter(phrases: preferences.hiddenPhrases))
      .keepingCurrentUserContent(using: accountsActor)
  }
}

extension FeedFilter where Content == <span class="hljs-class">Comment </span>{
  static func defaultPosts(
<span class="hljs-symbol">    preferences:</span> Preferences,
<span class="hljs-symbol">    accountsActor:</span> some SavedAccountsActor
  ) -&gt; some FeedFilter<span class="hljs-params">&lt;Content&gt;</span> {
    NSFWFilter(preferences: preferences)
      .run(
<span class="hljs-symbol">        before:</span> ContentMarkerFilter(
<span class="hljs-symbol">          accountsActor:</span> accountsActor,
<span class="hljs-symbol">          contentMarker:</span> .hiddenComments
        )
      )
      .run(
<span class="hljs-symbol">        before:</span> CommentExcludeIfNoRepliesFilter()
          .<span class="hljs-class">onlyRunIf </span>{ preferences.shouldFilterUnrepliedComments }
      )
      .run(before: HiddenPhrasesFilter(phrases: preferences.hiddenPhrases))
      .keepingCurrentUserContent(using: accountsActor)
  }
}

<span class="hljs-comment">// More content types...</span>
        </code></pre>
        <p>
          Adding new filtering logic was incredibly easy, because
          <code>FeedFilter</code> was easy to conform to. The filters were also
          relatively isolated from each other, and only handled a small concern,
          which made them easy to compose together into a larger and more
          meaningful filter.
        </p>
        <h4 id="composing-persistence-based-protocols">
          Composing Persistence Based Protocols
        </h4>
        <p>
          Now for a point of consideration, would you do what we did above with
          <code>NotesRepository</code>?
        </p>
        <p>
          Likely not because in many cases you would find it cumbersome, but
          you’re probably also questioning why we would need to in the first
          place. <code>NotesRepository</code> is an interface that implements IO
          for notes, and that IO has a very specific data store (HTTP API, Local
          Database, etc.). Therefore, if the implementation is just simple IO
          calls, then why is there a need for the kind of composition that you
          see with <code>FeedFilter</code>?
        </p>
        <p>
          This point is true in most cases with persistence, but that doesn’t
          mean that you want to leave out options arbitrarily. As long as the
          overall operation can be split into different parts cleanly, you can
          still rely on this composition. In fact, persistence has many
          mechanisms which are composable.
        </p>
        <p>
          This composability is literally why I decided to author
          <a href="https://github.com/mhayes853/swift-operation">Swift Operation</a>, and that library actually provides a similar DSL as
          <code>FeedFilter</code> for <em>any async operation</em>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Post</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">query</span><span class="hljs-params">(<span class="hljs-keyword">for</span> id: ID)</span></span> -&gt; some <span class="hljs-type">QueryRequest</span>&lt;<span class="hljs-type">Post</span>, <span class="hljs-type">Query</span>.<span class="hljs-type">State</span>&gt; {
    <span class="hljs-type">Self</span>.$query(id: id).retry(limit: <span class="hljs-number">3</span>)
      .refetchOnChange(
        of: .connected(to: <span class="hljs-type">NWPathMonitorObserver</span>.startingShared())
      )
      .deduplicated()
  }

  @<span class="hljs-type">QueryRequest</span>
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">query</span><span class="hljs-params">(id: ID)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Post</span> {
    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <p>
          Just like that, we’ve configured retries, deduplication, and the
          ability to refetch the post when the user’s network connection flips
          from offline to online. This works for more than just network requests
          by the way, it also works for calls to Apple Frameworks, and much
          more.
        </p>
        <p>
          You can create new building blocks of functionality by conforming to
          the <code>OperationModifier</code> protocol. For instance, here’s a
          simple one that adds artificial delay to an operation. You might
          wonder why you want to do that. It turns out that in Xcode previews,
          it’s very helpful to add such delay to see what the user experiences
          when their connection is slow.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DelayModifier</span>&lt;<span class="hljs-title">Operation</span>: <span class="hljs-title">OperationRequest</span>&gt;: <span class="hljs-title">OperationModifier</span> </span>{
  <span class="hljs-keyword">let</span> delay: <span class="hljs-type">TimeInterval</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    using operation: <span class="hljs-type">Operation</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>&gt;
  ) async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span> {
    <span class="hljs-keyword">try</span> await context.delayer.delay(<span class="hljs-keyword">for</span>: delay)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await operation.run(isolation: isolation, <span class="hljs-keyword">in</span>: context, with: continuation)
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">OperationRequest</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">delay</span><span class="hljs-params">(<span class="hljs-keyword">for</span> duration: TimeInterval)</span></span> -&gt; <span class="hljs-type">ModifiedOperation</span>&lt;<span class="hljs-type">Self</span>, <span class="hljs-type">DelayModifier</span>&lt;<span class="hljs-type">Self</span>&gt;&gt; {
    <span class="hljs-keyword">self</span>.modifier(<span class="hljs-type">DelayModifier</span>(delay: duration))
  }
}

@<span class="hljs-type">QueryRequest</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">myQuery</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">SomeData</span> {
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">let</span> myQuery = $myQuery.delay(<span class="hljs-keyword">for</span>: <span class="hljs-number">0.3</span>)
        </code></pre>
        <p>
          In the demo app for Swift Operation, I’ve even implemented such a
          modifier that persists the results of each query to a database. This
          allowed me to create dev tools that provide the opportunity to inspect
          any query made by the app. Thanks to
          <a href="https://github.com/pointfreeco/sqlite-data">￼￼<code>SQLiteData</code>￼￼</a>, the results are even synced across iCloud, and can be viewed from
          any device.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">StatefulOperationRequest</span> <span class="hljs-title">where</span> <span class="hljs-title">Self</span>: <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">analyzed</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">ModifiedOperation</span>&lt;<span class="hljs-type">Self</span>, _AnalysisModifier&lt;<span class="hljs-type">Self</span>&gt;&gt; {
    <span class="hljs-keyword">self</span>.modifier(_AnalysisModifier())
  }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">_AnalysisModifier</span>&lt;
  <span class="hljs-title">Operation</span>: <span class="hljs-title">StatefulOperationRequest</span> &amp; <span class="hljs-title">Sendable</span>
&gt;: <span class="hljs-title">OperationModifier</span>, <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    using query: <span class="hljs-type">Operation</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>&gt;
  ) async <span class="hljs-keyword">throws</span>(<span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>) -&gt; <span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span> {
    @<span class="hljs-type">Dependency</span>(\.defaultDatabase) <span class="hljs-keyword">var</span> database
    @<span class="hljs-type">Dependency</span>(\.continuousClock) <span class="hljs-keyword">var</span> clock
    @<span class="hljs-type">Dependency</span>(<span class="hljs-type">ApplicationLaunch</span>.<span class="hljs-type">ID</span>.<span class="hljs-keyword">self</span>) <span class="hljs-keyword">var</span> launchId
    @<span class="hljs-type">Dependency</span>(\.uuidv7) <span class="hljs-keyword">var</span> uuidv7

    <span class="hljs-keyword">let</span> yields = <span class="hljs-type">Mutex</span>([<span class="hljs-type">Result</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>&gt;]())
    <span class="hljs-keyword">let</span> continuation = <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>&gt; {
      result,
      context <span class="hljs-keyword">in</span>
      yields.withLock { $<span class="hljs-number">0</span>.append(result) }
      continuation.yield(with: result, using: context)
    }

    <span class="hljs-keyword">var</span> result: <span class="hljs-type">Result</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>&gt;!
    <span class="hljs-keyword">let</span> time = await clock.measure {
      result = await <span class="hljs-type">Result</span> { @<span class="hljs-type">Sendable</span> () async <span class="hljs-keyword">throws</span>(<span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>) -&gt; <span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span> <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">try</span> await query.run(isolation: isolation, <span class="hljs-keyword">in</span>: context, with: continuation)
      }
    }

    <span class="hljs-keyword">let</span> analysis = <span class="hljs-type">OperationAnalysis</span>(
      id: <span class="hljs-type">OperationAnalysis</span>.<span class="hljs-type">ID</span>(uuidv7()),
      launchId: launchId,
      operation: query,
      operationRetryAttempt: context.operationRetryIndex,
      operationRuntimeDuration: time,
      yieldedResults: yields.withLock { $<span class="hljs-number">0</span> },
      finalResult: result
    )

    await withErrorReporting {
      <span class="hljs-keyword">try</span> await database.write {
        <span class="hljs-keyword">try</span> <span class="hljs-type">OperationAnalysisRecord</span>.insert { <span class="hljs-type">OperationAnalysisRecord</span>.<span class="hljs-type">Draft</span>(analysis) }.execute($<span class="hljs-number">0</span>)
      }
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> result.<span class="hljs-keyword">get</span>()
  }
}
        </code></pre>
        <p>
          <img src="./protocols-and-possibility/assets/dev-tools-1.png" alt="A screen showing a list of all operations performed by an app for a particular app launch.">
          <img src="./protocols-and-possibility/assets/dev-tools-2.png" alt="A screen showing the result of a specific operation with statistics on its total runtime, number of retry attempts, and run date.">
        </p>
        <p>
          Suffice to say, doing this would be incredibly difficult with larger
          protocols, as you would have to add the above behavior to each
          requirement. Since <code>OperationModifier</code> is a small protocol
          with a requirement, it was quite easy to add this Dev Tools interface,
          as well as artificial delay, and much more that you can find in the
          <a href="https://github.com/mhayes853/swift-operation">library</a>.
        </p>
        <br>
        <h2 id="addressing-alternatives-and-concerns">
          Addressing Alternatives and Concerns
        </h2>
        <p>
          Now that I’ve laid out the case against large protocols, I want to
          spend some time going through many strategies that I’ve seen in the
          community to address some of the problems of large protocols.
        </p>
        <h3 id="struct-based-interfaces">Struct Based Interfaces</h3>
        <p>
          For the point on mocking, one may try to turn to a mutable struct of
          closures instead of a protocol. PointFree has been the primary
          advocate for this style as far as I can tell, and the docs of
          <a href="https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/designingdependencies#Struct-based-dependencies">Swift Dependencies</a>
          even have an entire article on this.
        </p>
        <p>
          In other words, we would make <code>NotesRepository</code> a struct
          like this.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Dependencies

<span class="hljs-meta">@DependencyClient</span>
struct NotesRepository {
  var <span class="hljs-string">findNote:</span> <span class="hljs-meta">@Sendable</span> (_ <span class="hljs-string">id:</span> Int) async <span class="hljs-keyword">throws</span> -&gt; Note?
  var allNotes: <span class="hljs-meta">@Sendable</span> (_ <span class="hljs-string">notebookId:</span> Int?) async throws -&gt; [Note]
  var findNotes: <span class="hljs-meta">@Sendable</span> (_ <span class="hljs-string">text:</span> String) async <span class="hljs-keyword">throws</span> -&gt; [Note]
  var <span class="hljs-string">save:</span> <span class="hljs-meta">@Sendable</span> (<span class="hljs-string">note:</span> Note) async <span class="hljs-keyword">throws</span> -&gt; Void
  var <span class="hljs-string">moveNote:</span> <span class="hljs-meta">@Sendable</span> (from <span class="hljs-string">srcNotebookId:</span> Int, to <span class="hljs-string">dstNotebookId:</span> Int) async <span class="hljs-keyword">throws</span> -&gt; Void
  var <span class="hljs-string">deleteNote:</span> <span class="hljs-meta">@Sendable</span> (with <span class="hljs-string">ids:</span> Set&lt;Int&gt;) async <span class="hljs-keyword">throws</span> -&gt; Void
}
        </code></pre>
        <p>
          Now in tests, we can override specific properties to provide a mock
          value.
        </p>
        <pre><code class="lang-swift">
@<span class="hljs-type">Test</span>(<span class="hljs-string">"Shows Alert When Failing To Add Note"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">showsAlertWhenFailingToAddNote</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">try</span> await withDependencies {
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}
    $<span class="hljs-number">0</span>[<span class="hljs-type">NotesRepository</span>.<span class="hljs-keyword">self</span>].save = { <span class="hljs-number">_</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>() }
  } operation: {
    <span class="hljs-comment">// AddNoteFormModel would access NotesRepository through the</span>
    <span class="hljs-comment">// @Dependency property wrapper.</span>
    <span class="hljs-keyword">let</span> model = <span class="hljs-type">AddNoteFormModel</span>()
    model.title = <span class="hljs-string">"My note"</span>
    model.contents = <span class="hljs-string">"This is fun"</span>

    #expect(model.alert == <span class="hljs-literal">nil</span>)
    await model.submitted()
    #expect(model.alert == .failedToSaveNote)
  }
}
        </code></pre>
        <p>
          For the record, I used to use this style extensively. However, I often
          found myself repeating the same patterns of code over and over again
          for each test.
        </p>
        <pre><code class="lang-swift">
@<span class="hljs-type">Test</span>(<span class="hljs-string">"Something"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">something</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">try</span> await withDependencies {
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}
    $<span class="hljs-number">0</span>[<span class="hljs-type">NotesRepository</span>.<span class="hljs-keyword">self</span>].save = { <span class="hljs-number">_</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>() }
  } operation: {
    <span class="hljs-comment">// ...</span>
  }
}

@<span class="hljs-type">Test</span>(<span class="hljs-string">"Other Thing"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">otherThing</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">try</span> await withDependencies {
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}
    $<span class="hljs-number">0</span>[<span class="hljs-type">NotesRepository</span>.<span class="hljs-keyword">self</span>].save = { <span class="hljs-number">_</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>() }
  } operation: {
    <span class="hljs-comment">// ...</span>
  }
}

<span class="hljs-comment">// So on...</span>
        </code></pre>
        <p>
          Another annoying thing about this pattern has to do with spies
          explicitly. In many cases, you need to use something like
          <code>Mutex</code> to capture the value passed to the dependency
          closure, and repeating this is not very fun either.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Synchronization

@<span class="hljs-type">Test</span>(<span class="hljs-string">"Spy Save"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">spySave</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">let</span> savedNotes = <span class="hljs-type">Mutex</span>([<span class="hljs-type">Note</span>]())
  <span class="hljs-keyword">try</span> await withDependencies {
    $<span class="hljs-number">0</span>[<span class="hljs-type">NotesRepository</span>.<span class="hljs-keyword">self</span>].save = { note <span class="hljs-keyword">in</span>
      savedNotes.withLock { $<span class="hljs-number">0</span> = note }
    }
  } operation: {
    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <p>
          If we want to do domain specific asserts on the spied values, we’re
          also out of luck because we cannot just add an additional API to a
          struct of closures that returns a property captured by those closures.
        </p>
        <p>
          In the long run, I’ve often reduced duplication by creating simple and
          reusable mocks that conform to a small protocol.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FailingSaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeError</span>: <span class="hljs-title">Error</span> </span>{}
    <span class="hljs-keyword">throw</span> <span class="hljs-type">SomeError</span>()
  }
}

@<span class="hljs-type">MainActor</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpySaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-keyword">private</span>(<span class="hljs-keyword">set</span>) <span class="hljs-keyword">var</span> savedNotes = [<span class="hljs-type">Note</span>]()

  <span class="hljs-comment">// We also get domain-specific asserts with this approach.</span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">expectSaved</span><span class="hljs-params">(<span class="hljs-number">_</span> note: Note)</span></span> {
    #expect(<span class="hljs-keyword">self</span>.savedNotes.<span class="hljs-built_in">contains</span>(note))
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">self</span>.savedNotes.append(note)
  }
}
        </code></pre>
        <p>
          Despite these flaws, I actually think structs with closures are great
          for certain kinds of problems. For instance, I often find myself
          relying on bags of closures over creating a
          <code>Delegate</code> protocol because delegates are often created in
          an adhoc fashion.
        </p>
        <pre><code class="lang-swift">
extension HapticaExtension {
  public struct <span class="hljs-name">Delegate</span>: Sendable {
    public var <span class="hljs-name">onJavaScriptExceptionThrown</span>:
      <span class="hljs-function"><span class="hljs-params">(@Sendable (HapticaExtension, JavaScriptException) -&gt; Void)</span>?
    public var onMetadataUpdated: <span class="hljs-params">(@Sendable (HapticaExtension, HapticaExtensionMetadata) -&gt; Void)</span>?

    public init<span class="hljs-params">(
      onJavaScriptExceptionThrown: (
        @Sendable (HapticaExtension, HapticaExtension.JavaScriptException) -&gt; Void
      )? = <span class="hljs-literal">nil</span>,
      onMetadataUpdated: (@Sendable (HapticaExtension, HapticaExtensionMetadata) -&gt; Void)? = <span class="hljs-literal">nil</span>
    )</span> {
      self.onJavaScriptExceptionThrown = onJavaScriptExceptionThrown
      self.onMetadataUpdated = onMetadataUpdated
    }
  }
}</span>
        </code></pre>
        <p>
          In other words, if the operation is very adhoc and is tightly coupled
          to what the caller is doing (eg. The operation would change the
          caller’s state somehow.), then I think closures suit that use case
          better. Delegates are a perfect example of this, but many well-known
          APIs also accept simple closures such as initializer of
          <code>Task</code>.
        </p>
        <h3 id="small-protocols-are-a-lot-of-boilerplate">
          Small Protocols are a lot of Boilerplate
        </h3>
        <p>
          I won’t deny this, but a small increase in typing speed is not worth
          time lost in friction. That is to say, I would rather deal with a bit
          of boilerplate over not being able to rapidly iterate.
        </p>
        <p>
          Perhaps you may also be thinking that I would advocate for small types
          to conform to the small protocols. Naturally, this would result in an
          explosition of small classes.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span>
}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">APINoteSaver</span>: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-keyword">init</span>(api: <span class="hljs-type">NotesAPI</span>) {
    <span class="hljs-comment">// ...</span>
  }
}

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NoteDeleter</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deleteNote</span><span class="hljs-params">(with id: Int)</span></span> async <span class="hljs-keyword">throws</span>
}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">APINoteDeleter</span>: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-keyword">init</span>(api: <span class="hljs-type">NotesAPI</span>) {
    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <p>
          Obviously, this would get quite annoying. Thankfully, Swift lets you
          extend existing types to conform to protocols you own after the fact.
          In most cases, I usually have a larger class that implements many of
          these small protocols.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotesAPI</span> </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">NotesAPI</span>: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">NotesAPI</span>: <span class="hljs-title">NoteDeleter</span> </span>{
  <span class="hljs-comment">// ...</span>
}
        </code></pre>
        <p>
          Protocols represent possibilities, and so I would rather leave the
          option for either a small class or large class to conform to them. For
          instance, maybe we want to save the note to a local file as well as
          the backend when it’s saved. We can address that with a simple
          conformance without having to modify <code>NotesAPI</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FileSaver</span>&lt;<span class="hljs-title">Base</span>: <span class="hljs-title">NoteSaver</span>&gt;: <span class="hljs-title">NoteSaver</span> </span>{
  <span class="hljs-keyword">let</span> base: <span class="hljs-type">Base</span>
  <span class="hljs-keyword">let</span> baseURL: <span class="hljs-type">URL</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">try</span> await <span class="hljs-keyword">self</span>.base.save(note: note)
    <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(note)
      .write(to: <span class="hljs-keyword">self</span>.baseURL.appending(path: <span class="hljs-string">"<span class="hljs-subst">\(note.id)</span>.json"</span>)
  }
}

<span class="hljs-keyword">let</span> fileAPISaving = <span class="hljs-type">FileSaver</span>(base: <span class="hljs-type">NotesAPI</span>.shared)
        </code></pre>
        <p>
          With the old <code>NotesRepository</code> approach, our only
          reasonable option would’ve added this file saving functionality on to
          our 1 potentially massive conforming type.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DefaultNotesRepository</span>: <span class="hljs-title">NotesRepository</span> </span>{
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> api: <span class="hljs-type">NotesAPI</span>
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> baseURL: <span class="hljs-type">URL</span>

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

  <span class="hljs-keyword">init</span>(api: <span class="hljs-type">NotesAPI</span>, <span class="hljs-keyword">let</span> baseURL: <span class="hljs-type">URL</span>) {
    <span class="hljs-comment">// ...</span>
  }

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

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">save</span><span class="hljs-params">(note: Note)</span></span> async <span class="hljs-keyword">throws</span> {
    <span class="hljs-keyword">try</span> await <span class="hljs-keyword">self</span>.api.save(note: note)
    <span class="hljs-keyword">try</span> <span class="hljs-type">JSONEncoder</span>().encode(note)
      .write(to: <span class="hljs-keyword">self</span>.baseURL.appending(path: <span class="hljs-string">"<span class="hljs-subst">\(note.id)</span>.json"</span>)
  }

  <span class="hljs-comment">// ...</span>
}
        </code></pre>
        <p>
          I want to make clear that I’m not necessarily advocating for the
          small-conformance style as the best way to implement local persistence
          on top of an HTTP API. In most cases, I would directly implement it
          alongside the API call in a larger conformance because that would be
          easier to understand.
        </p>
        <p>
          However, small protocols leaves open small conformances as an
          <em>option</em>, and that option may fit your context better than
          integrating it directly inside <code>DefaultNotesRepository</code>. If
          we had more requirements to implement behind the
          <code>NotesRepository</code> protocol, then it’s likely that we would
          have to keep bloating <code>DefaultNotesRepository</code>. If the
          implementation starts to become a bit bloated, it may be easier to add
          new behavior to the overall operation of saving a note with a smaller
          conformance.
        </p>
        <p>
          Sometimes, an explosion of small conforming types is what we want (eg.
          SwiftUI Views), but other times we want larger and more cohesive
          conforming types. Small protocols give you both options, whereas
          larger ones don’t.
        </p>
        <h3 id="you-can-just-autogenerate-mocks-with-a-package-mockingbird-swiftymocky-etc-">
          You can just Autogenerate Mocks with a Package (Mockingbird,
          SwiftyMocky, etc.)
        </h3>
        <p>
          Outside of the obvious dependency problem this has, the other pain
          point is that these kinds of packages tend to own the entire
          generation pipeline for the mocks, and they often cannot be modified
          after the fact. I actually think that this is <em>worse</em> than
          spending the time to create your own larger and global mock. At least
          the “one large mock” can be edited by its creators to accommodate
          different scenarios.
        </p>
        <h3 id="mocks-are-bad-lmao">Mocks are Bad Lmao</h3>
        <p>
          In many cases this is true, and I certainly don’t advocate for mocking
          everything. Without making this an entire piece on my opinion on
          mocks,
          <em>sometimes it’s just ok to use them as a weapon in your arsenal</em>. In fact, when I do mock a protocol for 1 suite of tests, I’ll often
          have another suite of tests that test the live implementation of the
          mocked protocol.
        </p>
        <p>
          Furthermore, I don’t want “mocking” to be the main idea that comes out
          of this piece. Smaller protocols certainly make mocking easier, but
          the larger idea is <em>possibility</em>. Mocking itself is a
          possibility.
        </p>
        <h3 id="what-if-i-need-a-large-protocol-">
          What if I need a Large Protocol?
        </h3>
        <p>Then make one.</p>
        <p>
          Alternatively, you can create larger protocols by composing smaller
          ones. This is how <code>Codable</code> is done, and you can do it with
          your own protocols too.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">typealias</span> <span class="hljs-type">NotesRepository</span> = <span class="hljs-type">NoteSaver</span> &amp; <span class="hljs-type">NoteDeleter</span> &amp; <span class="hljs-type">NotesFinder</span>

<span class="hljs-comment">// OR Alternatively (this requires conforming types to explicitly</span>
<span class="hljs-comment">// conform to NotesRepository)</span>
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">NotesRepository</span>: <span class="hljs-title">NoteSaver</span>, <span class="hljs-title">NoteDeleter</span>, <span class="hljs-title">NotesFinder</span> </span>{}
        </code></pre>
        <h3 id="when-should-i-use-a-large-protocol-">
          When should I use a Large Protocol?
        </h3>
        <p>The answer to this largely depends on how its is used.</p>
        <p>Take this scenario.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">Large</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">r1</span><span class="hljs-params">()</span></span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">r2</span><span class="hljs-params">()</span></span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">r3</span><span class="hljs-params">()</span></span>
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">r4</span><span class="hljs-params">()</span></span>
  <span class="hljs-comment">// All the way to r8...</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">foo</span><span class="hljs-params">(using large: some Large)</span></span> {
  large.r1()
  large.r2()
  large.r3()
  large.r4()
  <span class="hljs-comment">// All the way to r8...</span>
}
        </code></pre>
        <p>
          Since <code>foo</code> uses all the requirements from
          <code>Large</code>, then <code>Large</code> is a properly sized
          protocol because there is no unnecessary dependence. Additionally, we
          may not need a ton of possibility towards how <code>Large</code> is
          implemented, so in this case a large protocol is a decent trade-off.
        </p>
        <p>
          Another thing to consider on the unnecessary dependence point is that
          the protocol has a cohesive set of requirements. For instance.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">ScheduleableAlarm</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">Authorizer</span>: <span class="hljs-title">AnyObject</span>, <span class="hljs-title">Sendable</span> </span>{
    associatedtype <span class="hljs-type">Statuses</span>: <span class="hljs-type">AsyncSequence</span>&lt;<span class="hljs-type">ScheduleableAlarm</span>.<span class="hljs-type">AuthorizationStatus</span>, <span class="hljs-type">Never</span>&gt;

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">requestAuthorization</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">AuthorizationStatus</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">statuses</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Statuses</span>
  }

  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">AuthorizerKey</span>: <span class="hljs-title">DependencyKey</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> liveValue: any <span class="hljs-type">Authorizer</span> {
      <span class="hljs-comment">// ...</span>
    }
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">ScheduleableAlarm</span>.<span class="hljs-title">AuthorizationStatus</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">UpdatesKey</span>: <span class="hljs-title">SharedReaderKey</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> authorizer: any <span class="hljs-type">ScheduleableAlarm</span>.<span class="hljs-type">Authorizer</span>

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

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(
      context: LoadContext&lt;Value&gt;,
      subscriber: SharedSubscriber&lt;Value&gt;
    )</span></span> -&gt; <span class="hljs-type">SharedSubscription</span> {
      <span class="hljs-keyword">let</span> task = <span class="hljs-type">Task</span>.immediate {
        <span class="hljs-keyword">for</span> await status <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.authorizer.statuses() {
          withAnimation { subscriber.yield(status) }
        }
      }
      <span class="hljs-keyword">return</span> <span class="hljs-type">SharedSubscription</span> { task.cancel() }
    }
  }
}
        </code></pre>
        <p>
          In this case <code>UpdatesKey</code> only uses the
          <code>statuses</code> requirement from
          <code>ScheduleableAlarm.Authorizer</code>. Yet, I still think
          <code>ScheduleableAlarm.Authorizer</code> is properly sized for
          because its requirements are coherent (ie. Requesting authorization
          would be expected to have an impact on the status updates.), and
          because it is still easy to conform to (ie. It only has 2
          requirements.).
        </p>
        <p>
          Another thing could be that the protocol has default implementations
          for many of its requirements.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">QueryRequest</span>&lt;<span class="hljs-title">Value</span>, <span class="hljs-title">State</span>&gt;: <span class="hljs-title">QueryPathable</span>, <span class="hljs-title">Sendable</span>
<span class="hljs-title">where</span> <span class="hljs-title">State</span>.<span class="hljs-title">QueryValue</span> == <span class="hljs-title">Value</span> </span>{
  associatedtype <span class="hljs-type">Value</span>: <span class="hljs-type">Sendable</span>
  associatedtype <span class="hljs-type">State</span>: <span class="hljs-type">QueryStateProtocol</span> = <span class="hljs-type">QueryState</span>&lt;<span class="hljs-type">Value</span>?, <span class="hljs-type">Value</span>&gt;

  <span class="hljs-keyword">var</span> _debugTypeName: <span class="hljs-type">String</span> { <span class="hljs-keyword">get</span> }

  <span class="hljs-keyword">var</span> path: <span class="hljs-type">QueryPath</span> { <span class="hljs-keyword">get</span> }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> QueryContext)</span></span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    <span class="hljs-keyword">in</span> context: QueryContext,
    with continuation: QueryContinuation&lt;Value&gt;
  )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Value</span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">QueryRequest</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> _debugTypeName: <span class="hljs-type">String</span> { typeName(<span class="hljs-type">Self</span>.<span class="hljs-keyword">self</span>) }

  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> QueryContext)</span></span> {
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">QueryRequest</span> <span class="hljs-title">where</span> <span class="hljs-title">Self</span>: <span class="hljs-title">Hashable</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> path: <span class="hljs-type">QueryPath</span> {
    <span class="hljs-type">QueryPath</span>(<span class="hljs-keyword">self</span>)
  }
}
        </code></pre>
        <p>
          In this case, the only real requirement that a conformance often needs
          to implement is <code>fetch</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Post</span> </span>{
  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Query</span>: <span class="hljs-title">QueryRequest</span>, <span class="hljs-title">Hashable</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
      <span class="hljs-keyword">in</span> context: QueryContext,
      with continuation: QueryContinuation&lt;Post&gt;
    )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Post</span> {
      <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://jsonplaceholder.typicode.com/posts/<span class="hljs-subst">\(id)</span>"</span>)!
      <span class="hljs-keyword">let</span> (data, <span class="hljs-number">_</span>) = <span class="hljs-keyword">try</span> await <span class="hljs-type">URLSession</span>.shared.data(from: url)
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">Post</span>.<span class="hljs-keyword">self</span>, from: data)
    }
  }
}
        </code></pre>
        <h3 id="making-large-protocols-composable">
          Making Large Protocols Composable
        </h3>
        <p>
          You may be surprised to hear that it’s still possible to achieve much
          of the compositional benefits of smaller protocols with large
          protocols as well if you’re willing to write an entire modifier
          system.
        </p>
        <p>
          Swift Operation takes this approach because the
          <code>OperationRequest</code> and
          <code>StatefulOperationRequest</code> protocols have a bunch of
          annoying requirements that don’t need to be considered when
          implementing composable behavior.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">OperationRequest</span>&lt;<span class="hljs-title">Value</span>, <span class="hljs-title">Failure</span>&gt; </span>{
  associatedtype <span class="hljs-type">Value</span>
  associatedtype <span class="hljs-type">Failure</span>: <span class="hljs-type">Error</span>

  <span class="hljs-keyword">var</span> _debugTypeName: <span class="hljs-type">String</span> { <span class="hljs-keyword">get</span> }
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> OperationContext)</span></span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Value</span>, <span class="hljs-type">Failure</span>&gt;
  ) async <span class="hljs-keyword">throws</span>(<span class="hljs-type">Failure</span>) -&gt; <span class="hljs-type">Value</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">StatefulOperationRequest</span>&lt;<span class="hljs-title">State</span>&gt;: <span class="hljs-title">OperationRequest</span>
<span class="hljs-title">where</span> <span class="hljs-title">Value</span>: <span class="hljs-title">Sendable</span>, <span class="hljs-title">State</span>.<span class="hljs-title">OperationValue</span> == <span class="hljs-title">Value</span>, <span class="hljs-title">State</span>.<span class="hljs-title">Failure</span> == <span class="hljs-title">Failure</span> </span>{
  associatedtype <span class="hljs-type">State</span>: <span class="hljs-type">OperationState</span>
  <span class="hljs-keyword">var</span> path: <span class="hljs-type">OperationPath</span> { <span class="hljs-keyword">get</span> }
}
        </code></pre>
        <p>
          If you were to implement composable behavior with
          <code>OperationRequest</code>, chances are that you would forget to
          forward <code>_debugTypeName</code>, <code>_path</code>, or
          <code>setup</code>. This is why the library defines a simpler
          <code>OperationModifier</code> protocol.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">OperationModifier</span>&lt;<span class="hljs-title">Value</span>, <span class="hljs-title">Failure</span>&gt; </span>{
  associatedtype <span class="hljs-type">Operation</span>: <span class="hljs-type">OperationRequest</span>
  associatedtype <span class="hljs-type">Value</span>
  associatedtype <span class="hljs-type">Failure</span>: <span class="hljs-type">Error</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> OperationContext, using operation: Operation)</span></span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    using operation: <span class="hljs-type">Operation</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Value</span>, <span class="hljs-type">Failure</span>&gt;
  )
}
        </code></pre>
        <p>
          The library then provides a query called
          <code>ModifiedOperation</code>, which does the hard work of adapting
          your <code>OperationModifier</code> to work with an
          <code>OperationRequest</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ModifiedOperation</span>&lt;
  <span class="hljs-title">Operation</span>: <span class="hljs-title">OperationRequest</span>,
  <span class="hljs-title">Modifier</span>: <span class="hljs-title">OperationModifier</span>
&gt;: <span class="hljs-title">OperationRequest</span> <span class="hljs-title">where</span> <span class="hljs-title">Modifier</span>.<span class="hljs-title">Operation</span> == <span class="hljs-title">Operation</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">typealias</span> <span class="hljs-type">Value</span> = <span class="hljs-type">Modifier</span>.<span class="hljs-type">Value</span>

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> operation: <span class="hljs-type">Operation</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> modifier: <span class="hljs-type">Modifier</span>

  @inlinable
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> _debugTypeName: <span class="hljs-type">String</span> {
    <span class="hljs-keyword">self</span>.operation._debugTypeName
  }

  @inlinable
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> OperationContext)</span></span> {
    <span class="hljs-keyword">self</span>.modifier.setup(context: &amp;context, using: <span class="hljs-keyword">self</span>.operation)
  }

  @inlinable
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Modifier</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Modifier</span>.<span class="hljs-type">Failure</span>&gt;
  ) async <span class="hljs-keyword">throws</span>(<span class="hljs-type">Modifier</span>.<span class="hljs-type">Failure</span>) -&gt; <span class="hljs-type">Modifier</span>.<span class="hljs-type">Value</span> {
    <span class="hljs-keyword">try</span> await <span class="hljs-keyword">self</span>.modifier.run(
      isolation: isolation,
      <span class="hljs-keyword">in</span>: context,
      using: <span class="hljs-keyword">self</span>.operation,
      with: continuation
    )
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">ModifiedOperation</span>: <span class="hljs-title">StatefulOperationRequest</span>
<span class="hljs-title">where</span>
  <span class="hljs-title">Modifier</span>.<span class="hljs-title">Operation</span>: <span class="hljs-title">StatefulOperationRequest</span>,
  <span class="hljs-title">Operation</span>.<span class="hljs-title">Value</span> == <span class="hljs-title">Modifier</span>.<span class="hljs-title">Value</span>,
  <span class="hljs-title">Operation</span>.<span class="hljs-title">Failure</span> == <span class="hljs-title">Modifier</span>.<span class="hljs-title">Failure</span>
</span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">typealias</span> <span class="hljs-type">State</span> = <span class="hljs-type">Operation</span>.<span class="hljs-type">State</span>

  @inlinable
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> path: <span class="hljs-type">OperationPath</span> {
    <span class="hljs-keyword">self</span>.operation.path
  }
}
        </code></pre>
        <p>
          SwiftUI also takes this approach for adapting composability for views.
          For instance, the <code>View</code> protocol from SwiftUI actually
          looks something like this.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">View</span> </span>{
  nonisolated <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">_makeView</span><span class="hljs-params">(view: _GraphValue&lt;<span class="hljs-keyword">Self</span>&gt;, inputs: _ViewInputs)</span></span> -&gt; _ViewOutputs

  nonisolated <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">_makeViewList</span><span class="hljs-params">(view: _GraphValue&lt;<span class="hljs-keyword">Self</span>&gt;, inputs: _ViewListInputs)</span></span> -&gt; _ViewListOutputs

  nonisolated <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">_viewListCount</span><span class="hljs-params">(inputs: _ViewListCountInputs)</span></span> -&gt; <span class="hljs-type">Int</span>?

  associatedtype <span class="hljs-type">Body</span>: <span class="hljs-type">View</span>

  @<span class="hljs-type">ViewBuilder</span>
  @<span class="hljs-type">MainActor</span>
  @preconcurrency
  <span class="hljs-keyword">var</span> body: <span class="hljs-type">Self</span>.<span class="hljs-type">Body</span> { <span class="hljs-keyword">get</span> }
}
        </code></pre>
        <p>
          However, you don’t ever implement those top 3 requirements. In fact,
          you may not even have known about their existence because Xcode
          doesn’t show them! Instead, a few select primitive views from SwiftUI
          (eg. <code>Text</code>) implement those requirements whilst a default
          implementation is used for the typical views that you create in your
          app. Even if you tried to implement those requirements, chances are is
          that it would be impossible because they use types that are largely
          internal to SwiftUI.
        </p>
        <p>
          This is why SwiftUI provides a <code>ViewModifier</code> and the
          <code>ModifiedContent</code> view if you want to add composable
          behavior to your views. <code>ModifiedContent</code> is a special view
          that adapts your <code>ViewModifier</code> into a legitimate SwiftUI
          view by providing proper implementations of
          <code>_viewListCount</code>, <code>_makeViewList</code>, and
          <code>_makeView</code> based on the child content in the view.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ModifiedContent</span>&lt;
  <span class="hljs-title">Content</span>: <span class="hljs-title">View</span>,
  <span class="hljs-title">Modifier</span>: <span class="hljs-title">ViewModifier</span>
&gt;: <span class="hljs-title">View</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> content: <span class="hljs-type">Content</span>
  <span class="hljs-keyword">public</span> <span class="hljs-keyword">let</span> modifier: <span class="hljs-type">Modifier</span>

  <span class="hljs-comment">// See: https://github.com/OpenSwiftUIProject/OpenSwiftUI/blob/main/Sources/OpenSwiftUICore/Modifier/ViewModifier.swift</span>

  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">_makeView</span><span class="hljs-params">(
    view: _GraphValue&lt;<span class="hljs-keyword">Self</span>&gt;,
    inputs: _ViewInputs
  )</span></span> -&gt; _ViewOutputs {
    <span class="hljs-type">Modifier</span>.makeDebuggableView(
      modifier: view[offset: { .of(&amp;$<span class="hljs-number">0</span>.modifier) }],
      inputs: inputs
    ) {
      <span class="hljs-type">Content</span>.makeDebuggableView(
        view: view[offset: { .of(&amp;$<span class="hljs-number">0</span>.content) }],
        inputs: $<span class="hljs-number">1</span>
      )
    }
  }

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

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">View</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">modifier</span>&lt;Modifier&gt;<span class="hljs-params">(
    <span class="hljs-number">_</span> modifier: Modifier
  )</span></span> -&gt; <span class="hljs-type">ModifiedContent</span>&lt;<span class="hljs-type">Self</span>, <span class="hljs-type">Modifier</span>&gt; {
    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <br>
        <h2 id="conclusion">Conclusion</h2>
        <p>
          The more possibilities that your protocol provides, the easier it is
          to add new features, perform one-off experiments, and make changes to
          your code. Possibility comes by making your protocols easy to conform
          to, which often means making them small with simpler requirements.
        </p>
        <p>
          When you have possibility, it doesn’t matter if a “god” class, a tiny
          one-off handwritten mock, or an entire DSL implements the protocol.
          The important part is that you have the possibility to do any of those
          things in the first place.
        </p>
        <p>
          Unnecessary dependence on large protocols hinders the amount of
          possibility for no good reason because it requires a larger than
          necessary conformance. Furthermore, a large protocols with a bunch of
          requirements, such as a “Repository” protocol, also hinders
          possibility because there is more friction when conforming to larger
          protocols.
        </p>
        <p>
          However, if a large protocol can counter those 2 points in your
          context, then feel free to create one. Additionally, you may find that
          you can create a large protocol by composing it from smaller
          protocols.
        </p>
        <p>
          Lastly, you should check out
          <a href="https://github.com/mhayes853/swift-operation">Swift Operation</a>, it handles a lot of pain around asynchronous state management and
          data fetching for you.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Swift Cactus, an Alternative to Foundation Models</title>
      <link>https://whypeople.xyz/swift-cactus</link>
      <guid isPermaLink="true">https://whypeople.xyz/swift-cactus</guid>
      <pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate>
      <description>More model choice, works on any OS version, on any Apple Device.</description>
      <content:encoded><![CDATA[<h1 id="swift-cactus-an-alternative-to-foundation-models" class="title">
          Swift Cactus, an Alternative to Foundation Models
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          Given that Foundation Models has had a positive reception since it was
          introduced earlier this year at WWDC, what could be so wrong with it?
        </p>
        <p>
          Let’s sum up all of the problems I have with Foundation Models in one
          sentence.
          <em>It’s practically impossible to put FoundationModels at the center
            of your app’s experience.</em>
        </p>
        <p>
          Now let’s see why that is the case, and why I created
          <a href="https://github.com/mhayes853/swift-cactus">swift-cactus</a>
          to begin solving this problem.
        </p>
        <h2 id="problems-with-foundation-models">
          Problems with Foundation Models
        </h2>
        <p>
          One of the obvious answers is that it requires a device that supports
          Apple Intelligence, which for iPhones is really only the everything
          from iPhone 15 Pro and beyond. This is extremely limiting, and as a
          developer it makes it so that you can’t reliably base your product
          around the framework. Well, that is unless you want to alienate ~80%
          (rough estimate) of your potential market.
        </p>
        <p>
          iPad models are a bit more in luck with any M-series iPad getting
          support automatically, but note that this article is being written on
          an 11inch iPad Pro from 2018 (it still runs really well to this day).
          Additionally, mac users are in a far more optimal spot given that the
          M1 processor was introduced ~5 years ago at this point. However, it’s
          no secret that most Apple development happens on iOS, yet many of it
          devices are left out.
        </p>
        <p>
          Additionally, Foundation Models only ships a singular 3B parameter
          model that you’re forced to use nearly as is (outside of adapters). Of
          course, this really means that your options are limited with the
          framework. That lack of optionality probably isn’t a problem for small
          indie to mid-sized apps that have a non-AI primary value proposition.
          However, I think those trying to build incredibly sophisticated apps
          using offline models in very scientific or highly intellectual domains
          (eg. Health) may find this lack of optionality to be problematic.
        </p>
        <p>
          Lastly, given that this is an Apple-specific framework, newer APIs
          added to the framework won’t be available on previous OS versions. In
          fact, you can’t even use Foundation Models below iOS 26, so it will
          probably quite some time before it gains any sort of reliable adoption
          outside of early adopters.
        </p>
        <p>
          I think the why behind the problem here is that Foundation Models is
          far too limited, and more is needed here if we want to build great
          privacy-aware apps that involve AI at the center of their experience.
          Right now, I can only see it as a framework that indie devs or small
          teams adopt in their apps, and even then it still isn’t reliable
          enough to be at the center of their app’s experience due to hardware
          limitations.
        </p>
        <p>
          Eventually (in ~2-3 years), the hardware limitation won’t be as much
          of a problem, but you’ll still likely be limited in model options
          whilst still having to deal with newer API compatibility issues.
        </p>
        <h2 id="swift-cactus">Swift Cactus</h2>
        <p>
          <a href="https://github.com/mhayes853/swift-cactus">Swift Cactus</a>
          is a wrapper over the
          <a href="https://github.com/cactus-compute/cactus">cactus compute</a>
          inference engine, which at the time of writing this seems to be the
          fastest inference engine that I could find for ARM chips (ie. Mobile
          Devices). The company behind the engine is also YC backed, so we’ll
          see where this goes.
        </p>
        <p>
          The package provides an API for downloading cactus-supported models
          (Qwen and Gemma for now) into your application, telemetry with the
          cactus dashboard, and a minimal wrapper around the C cactus FFI. The C
          FFI includes an embeddings API, which isn’t available at all in
          Foundation Models. The library runs on all Apple platforms, including
          watchOS, where Foundation Models lacks total support.
        </p>
        <p>
          Like all other articles on libraries I create, examples of how to use
          the library can be found in the repo itself. This article will focus
          on design.
        </p>
        <h3 id="limitations">Limitations</h3>
        <p>
          The API isn’t nearly as high-level as Foundation Models, but I think
          the essential building blocks are there. For instance, tool calling is
          supported, but there’s no fancy <code>Tool</code> protocol, rather you
          have to execute the tool calling loop yourself. Neither is there any
          fancy <code>@Generable</code> macro yet, and you also have to ensure
          entire responses from the model fit inside of a fixed size buffer as
          the C FFI requires you to as of now. I think with more time, these
          limitations can be overcome by providing a higher level wrapper API
          over the existing minimal API.
        </p>
        <p>
          Additionally, the <code>CactusLanguageModel</code> class isn’t
          thread-safe because the C FFI isn’t fully thread-safe either, and all
          of its methods are synchronous and blocking. While this gives you
          flexibility, it also makes it easy to misuse the model (such as
          running it on the main thread). I recommend that you solve this by
          creating a dedicated actor to house the model instance.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LanguageModelActor</span> </span>{
  <span class="hljs-keyword">let</span> model: <span class="hljs-type">CactusLanguageModel</span>

  <span class="hljs-keyword">init</span>(model: sending <span class="hljs-type">CactusLanguageModel</span>) {
    <span class="hljs-keyword">self</span>.model = model
  }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withIsolation</span>&lt;T, E: Error&gt;<span class="hljs-params">(
    perform operation: <span class="hljs-params">(isolated <span class="hljs-keyword">Self</span>)</span></span></span> <span class="hljs-keyword">throws</span>(<span class="hljs-type">E</span>) -&gt; sending <span class="hljs-type">T</span>
  ) <span class="hljs-keyword">throws</span>(<span class="hljs-type">E</span>) -&gt; sending <span class="hljs-type">T</span> {
    <span class="hljs-keyword">try</span> operation(<span class="hljs-keyword">self</span>)
  }
}

@concurrent
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">chatInBackground</span><span class="hljs-params">(with modelActor: LanguageModelActor)</span></span> async <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">try</span> await modelActor.withIsolation { modelActor <span class="hljs-keyword">in</span>
    <span class="hljs-comment">// You can access the model directly because the closure is isolated</span>
    <span class="hljs-comment">// to modelActor</span>
    <span class="hljs-keyword">let</span> model = modelActor.model

    <span class="hljs-comment">// ...</span>
  }
}
        </code></pre>
        <p>
          Lastly, you’re limited to models that the cactus engine directly
          supports, which isn’t a lot right now. However, you have more options
          than Foundation Models, and more will be added in the future. At the
          very least, the current options you have perform well on a wider range
          of devices than what Foundation Models supports.
        </p>
        <h3 id="embeddings">Embeddings</h3>
        <p>
          Foundation Models currently doesn’t have support for generating a
          vector of embeddings from a string of text. Embeddings can be used to
          match similar pieces of text, and a common way to do this is through
          checking the cosine similarity score.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Cactus

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cosineSimilarity</span>&lt;C: Collection&gt;<span class="hljs-params">(<span class="hljs-number">_</span> a: C, <span class="hljs-number">_</span> b: C)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Double</span>
<span class="hljs-keyword">where</span> <span class="hljs-type">C</span>.<span class="hljs-type">Element</span>: <span class="hljs-type">BinaryFloatingPoint</span> {
  <span class="hljs-keyword">guard</span> a.<span class="hljs-built_in">count</span> == b.<span class="hljs-built_in">count</span> <span class="hljs-keyword">else</span> {
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LengthError</span>: <span class="hljs-title">Error</span> </span>{}
    <span class="hljs-keyword">throw</span> <span class="hljs-type">LengthError</span>()
  }
  <span class="hljs-keyword">var</span> dot = <span class="hljs-number">0.0</span>, normA = <span class="hljs-number">0.0</span>, normB = <span class="hljs-number">0.0</span>
  <span class="hljs-keyword">var</span> ia = a.startIndex, ib = b.startIndex
  <span class="hljs-keyword">while</span> ia != a.endIndex {
    <span class="hljs-keyword">let</span> x = <span class="hljs-type">Double</span>(a[ia])
    <span class="hljs-keyword">let</span> y = <span class="hljs-type">Double</span>(b[ib])
    dot += x * y
    normA += x * x
    normB += y * y
    ia = a.index(after: ia)
    ib = b.index(after: ib)
  }
  <span class="hljs-keyword">let</span> denom = (normA.squareRoot() * normB.squareRoot())
  <span class="hljs-keyword">return</span> denom == <span class="hljs-number">0</span> ? <span class="hljs-number">0</span> : dot / denom
}

<span class="hljs-keyword">let</span> model = <span class="hljs-keyword">try</span> <span class="hljs-type">CactusLanguageModel</span>(from: modelURL)

<span class="hljs-keyword">let</span> fancy = <span class="hljs-keyword">try</span> model.embeddings(<span class="hljs-keyword">for</span>: <span class="hljs-string">"This is some fancy text"</span>)
<span class="hljs-keyword">let</span> pretty = <span class="hljs-keyword">try</span> model.embeddings(<span class="hljs-keyword">for</span>: <span class="hljs-string">"This is some pretty text"</span>)

<span class="hljs-built_in">print</span>(cosineSimilarity(fancy, pretty))
        </code></pre>
        <p>
          You can use the techniques such as cosine similarity to search through
          text in natural language. For instance, if you’re using
          <a href="https://github.com/pointfreeco/sqlite-data">SQLiteData</a>,
          you can use this as alternative to FTS5 to implement search for your
          application. Instead of maintaining an FTS virtual table, you would
          have to maintain a column that holds the embeddings for the content
          you want to search instead.
        </p>
        <p>
          Here’s a very simplified example of how you can use temporary triggers
          to maintain this column using SQLiteData.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Cactus
<span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> SQLiteData
<span class="hljs-keyword">import</span> Synchronization

@<span class="hljs-type">Table</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Reminder</span>: <span class="hljs-title">Identifiable</span> </span>{
  <span class="hljs-keyword">var</span> id: <span class="hljs-type">UUID</span>
  <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>

  @<span class="hljs-type">Column</span>(<span class="hljs-keyword">as</span>: [<span class="hljs-type">Float</span>].<span class="hljs-type">JSONRepresentation</span>.<span class="hljs-keyword">self</span>)
  <span class="hljs-keyword">var</span> embeddings: [<span class="hljs-type">Float</span>]
}

<span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> model = <span class="hljs-type">Mutex</span>(<span class="hljs-keyword">try</span>! <span class="hljs-type">CactusLanguageModel</span>(from: modelURL))

@<span class="hljs-type">DatabaseFunction</span>(<span class="hljs-keyword">as</span>: ((<span class="hljs-type">String</span>) -&gt; [<span class="hljs-type">Float</span>].<span class="hljs-type">JSONRepresentation</span>).<span class="hljs-keyword">self</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">embeddings</span><span class="hljs-params">(text: String)</span></span> -&gt; [<span class="hljs-type">Float</span>] {
  model.withLock { (<span class="hljs-keyword">try</span>? $<span class="hljs-number">0</span>.embeddings(<span class="hljs-keyword">for</span>: text)) ?? [] }
}

<span class="hljs-comment">// Triggers to ensure that embeddings are always created after inserts and updates.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">createEmbeddingsTriggers</span><span class="hljs-params">(<span class="hljs-keyword">in</span> db: Database)</span></span> <span class="hljs-keyword">throws</span> {
  <span class="hljs-keyword">try</span> <span class="hljs-type">Reminder</span>.createTemporaryTrigger(
    after: .insert { new <span class="hljs-keyword">in</span>
      <span class="hljs-type">Reminder</span>.<span class="hljs-built_in">find</span>(new.id)
        .update { $<span class="hljs-number">0</span>.embeddings = $embeddings(new.name) }
    }
  )
  .execute(db)

  <span class="hljs-keyword">try</span> <span class="hljs-type">Reminder</span>.createTemporaryTrigger(
    after: .update { <span class="hljs-number">_</span>, new <span class="hljs-keyword">in</span>
      <span class="hljs-type">Reminder</span>.<span class="hljs-built_in">find</span>(new.id)
        .update { $<span class="hljs-number">0</span>.embeddings = $embeddings(new.name) }
    } when: { old, new <span class="hljs-keyword">in</span>
      new.name != old.name
    }
  )
  .execute(db)
}
        </code></pre>
        <h3 id="telemetry">Telemetry</h3>
        <p>
          Swift Cactus also ships with support for telemetry such that you can
          analyze model performance and registered devices in the cactus
          dashboard. Currently, the default telemetry integration is only
          supported on iOS and macOS, but you can also build your own custom
          integration if you wish. (This is because there seems to be a
          proprietary binary that Cactus uses to decrypt the string that
          contains the device id, and I could only find XCFrameworks for iOS and
          macOS. I could also choose to reverse engineer the solution since it
          seems like the decryption key is in the compiled binary, but I don’t
          want to get in trouble.)
        </p>
        <p>
          Telemetry is completely optional, and events are recorded
          automatically by the <code>CactusLanguageModel</code> class. All you
          need to do to set it up is pass your token from the cactus dashboard
          to <code>CactusTelemetry</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Cactus

<span class="hljs-type">CactusTelementry</span>.configure(<span class="hljs-string">"&lt;your token here&gt;"</span>)
        </code></pre>
        <h2 id="an-alternative-foundation-models">
          An Alternative Foundation Models
        </h2>
        <p>
          Why provide an alternative to an Apple framework? Wouldn’t Apple
          always know what’s the best for their ecosystem?
        </p>
        <p>
          There are multiple successful third-party alternatives to some of
          Apple’s largest frameworks in my opinion. RevenueCat providing an
          alternative to StoreKit is probably the largest case of this,
          especially considering that RevenueCat is a venture backed company.
          Another good example is SQLiteData and GRDB competing directly with
          CoreData and SwiftData, both the of former are open source, and
          SQLiteData even uses GRDB under the hood. Additionally, Alamofire also
          provides a more robust alternative to <code>URLSession</code>.
        </p>
        <p>
          With local LLMs quickly becoming more prevalant at center of consumer
          apps, I think it’s only natural that alternatives to Foundation Models
          present themselves to address its limitations. Just as alternatives
          came in to address the limitations of CoreData and SwiftData (which
          are also at the center of many apps).
        </p>
        <p>
          Right now, at this present point in time, I don’t think Foundation
          Models is solving this problem well enough. Additionally, given
          Apple’s release cadences, I don’t think Foundation Models will solve
          this problem well enough for a while.
        </p>
        <p>
          There’s a lot of work to do for Swift Cactus to match the more
          convenient higher level API offered by Foundation Models. Even so, I
          think having access to the lower level functionality is beneficial
          regardless. However, just the already present lower-level
          functionality already makes bringing local LLMs to the center of your
          app’s experience possible for everyone, not just those on the latest
          iPhones and OS versions.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Swift Operation</title>
      <link>https://whypeople.xyz/swift-operation</link>
      <guid isPermaLink="true">https://whypeople.xyz/swift-operation</guid>
      <pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate>
      <description>A cross-platform library for flexible asynchronous state and operation management in Swift.</description>
      <content:encoded><![CDATA[<h1 id="swift-operation" class="title">Swift Operation</h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          Data fetching is flakey, yet most software we use needs to do it. In
          general, flakiness complicates software, because something can only be
          flakey if it comes from the physical-world. That is to say, fetching
          data is hard.
        </p>
        <p>
          However, when I say it’s hard, I don’t mean the code to actually fetch
          data itself. Generally, that part is pretty easy, and you can spin up
          an HTTP request to any server with just a few lines of Swift code.
          <code>Codable</code> and <code>JSONDecoder</code> handle most cases of
          decoding data from a server, and they do so pretty well. Additionally,
          many Apple Frameworks that also involve data fetching also tend to be
          relatively simple function calls if we’re just talking about fetching
          the data itself.
        </p>
        <p>
          Yet we know as developers that number of things that can go wrong when
          dealing with a function that has both <code>async</code> and
          <code>throws</code> in its signature. Loading states, error alerts,
          retries, caching, cancellation, deduplication, exponential backoff,
          delays, network connection status, keeping data consistent between
          screens and much more that is annoying to deal with.
        </p>
        <p>
          I’ve lost count of how many times I’ve written the same kind of code
          that: Sets a state variable to indicate loading, fetches data, if
          failed sets an error state variable, otherwise sets the data state
          variable to indicate that loading is done. The cherry on top is when
          you also have to display an alert when the data fetching fails. All of
          this is also not considering caching, yielding intermediate values
          whilst fetching from the source of truth (ie. Your backend), and much
          more.
        </p>
        <p>
          Over these past few months, I’ve spent a lot of effort developing a
          toolkit that I’ve used across various projects to make dealing with
          asynchronous operations and data fetching much more manageable, so you
          can move on after writing the simple data fetching code. Today, I’m
          finally putting a formal release tag behind these tools, so that you
          can use them in your (smaller) apps. For the record, that also
          includes apps on Linux, WASM, Windows, and Android.
        </p>
        <p>
          <strong>The resulting library is named
            <a href="https://github.com/mhayes853/swift-operation">￼<code>swift-operation</code>￼</a>.</strong>
        </p>
        <p>
          This article will mainly be about its development, and the various
          techniques I’ve employed when using and designing the library. I won’t
          focus too much on how to use the library in this article. The GitHub
          repo, documentation, and many demo apps present in the repo should do
          a sufficient job in providing many examples of how to use the library.
        </p>
        <br>
        <h2 id="origins">Origins</h2>
        <p>
          This project started out from my experience working with react-native
          in a startup for the past 1.5 years where we heavily relied on
          <a href="https://tanstack.com/query/latest/docs/framework/react/overview">Tanstack Query</a>. When writing Swift apps in my spare time, I missed the ability to
          have something that managed async state intuitively, which spawned the
          desire for this project. My initial goal was just to port Tanstack
          Query over to Swift, and integrate it with
          <a href="https://github.com/pointfreeco/swift-sharing">￼<code>swift-sharing</code>￼</a>, an already popular state management library used in many apps
          (including every app that uses modern TCA). In fact, the original name
          of the library was Swift Query, but of course that name was stolen…
        </p>
        <p>
          I was able to get this simple port version of the library up and
          running in quite a short period of time, but naturally I of course
          felt the need to continuously refine things as I kept trying it in
          different scenarios. This led to months of iteration as the library
          needed to find a distinct identity from Tanstack Query given that
          Swift is a very different language compared to TypeScript.
        </p>
        <p>
          Tanstack Query advertises itself as an “Asynchronous State Manager”.
          Swift Operation also has such state management built in, and even has
          an integration with Sharing through the
          <code>@SharedOperation</code> property wrapper. However, the resulting
          library I’ve built serves a more general purpose. Swift Operation aims
          to make asynchronous work as whole easier to manage, of which
          “asynchronous state management” would be a subset of that
          functionality. I like to call Swift Operation, an asynchronous
          operation manager because it uses a SwiftUI modifier approach to
          constructing operations.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span>: <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>
  <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">User</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">query</span><span class="hljs-params">(<span class="hljs-keyword">for</span> id: Int)</span></span> -&gt; some <span class="hljs-type">QueryRequest</span>&lt;<span class="hljs-type">User</span>?, any <span class="hljs-type">Error</span>&gt; {
    <span class="hljs-comment">// We've configured fetching our user with retries, deduplication,</span>
    <span class="hljs-comment">// stale-when-revalidate, and automatic refetching when the network</span>
    <span class="hljs-comment">// connection flips from offline to online with just a few lines of </span>
    <span class="hljs-comment">// code.</span>
    <span class="hljs-type">Query</span>(id: id)
      .retry(limit: <span class="hljs-number">3</span>)
      .deduplicated()
      .stale(after: <span class="hljs-number">60</span> * <span class="hljs-number">5</span>)
      .rerunOnChange(of: .connected(to: <span class="hljs-type">NWPathMonitorObserver</span>.startingShared()))
  }

  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Query</span>: <span class="hljs-title">QueryRequest</span>, <span class="hljs-title">Hashable</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
      isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?
      <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
      with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">User</span>?, any <span class="hljs-type">Error</span>&gt;
    ) async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">User</span>? {
      <span class="hljs-comment">// Fetch the user...</span>
    }
  }
}
        </code></pre>
        <p>
          For TypeScript apps, Tanstack Query is generally only used inside your
          UI layer, and its API heavily skews itself towards that particular use
          case. Swift Operation provides tools to use the library both within
          your UI and data layers. For example, there are many instances where
          the library is used for
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CanIClimb/CanIClimbKit/Sources/CanIClimbKit/LocationCore/UserLocationTool.swift">tool calling</a>
          via conforming to the <code>Tool</code> protocol conformances from
          Apple’s new FoundationModels framework.
        </p>
        <p>
          I suppose if you want to correlate Swift Operation with libraries from
          the TypeScript world, it’s largely a combination of both Tanstack
          Query and <a href="https://effect.website/">Effect</a>. Though so far,
          the library is more optimized for the Tanstack Query case.
        </p>
        <br>
        <h2 id="design-philosophy-learning-curve">
          Design Philosophy &amp; Learning Curve
        </h2>
        <p>
          Swift Operation is a complicated library, and intentionally so. I
          would wager that it is more complicated than Tanstack Query if you
          were to try and understand every detail of it.
        </p>
        <p>
          That being said, it is also quite simple in the sense that it’s built
          entirely around the notion of calling an async function. I suppose the
          complexity is really just case of “different” rather than “seriously
          complicated” (I think of TCA’s complexity in the same way for the
          record). Once you get over the learning curve, I think the end result
          will be a net win in simplicity considering what the library does for
          you.
        </p>
        <p>This large learning curve derives from 3 things:</p>
        <ol>
          <li>
            The library is incredibly flexible whilst also providing as much
            type-safety as possible.
          </li>
          <li>
            It surfaces advanced concepts of Swift Concurrency (eg.
            <code>isolated</code> parameters) right in your face.
          </li>
          <li>
            Data fetching is hard and complex with many edge cases that the
            library wants you to be aware of.
          </li>
        </ol>
        <p>
          Though the good news is that the most common use case (ie. Just making
          and using a <code>QueryRequest</code> that fetches some data in your
          UI.) is not that hard, and can be done incredibly “swiftly”. This most
          common use case should get you through ~80% of scenarios, and the rest
          of the library can be progressively learned.
        </p>
        <p>
          You’ll likely end up with more lines of code in your app after
          adopting the library. Ignoring the fact that many of those added lines
          won’t contain much substance (ie. Struct/Function declarations), the
          library also wants to surface edge cases that you probably weren’t
          thinking about before, so it’s only natural that you will end up
          writing code to handle those additional cases. Of course, over time
          the ideal for the library should be to get rid of many of those added
          lines that have little substance.
        </p>
        <br>
        <h2 id="comparison-to-tanstack-query">Comparison to Tanstack Query</h2>
        <p>
          One of the things Tanstack Query could’ve improved on was what the
          notion of a “query” was. In fact, I named the library Swift Operation
          because queries are a subset of a general operation that runs some
          workflow and returns a result. Mutations and paginated requests (ie.
          The equivalent of Infinite Queries from Tanstack Query), are also
          subsets of operations. In Tanstack Query, mutations and queries are
          separate concepts, but in Swift Operation they share much of the same
          core since they both derive from base operations.
        </p>
        <h3 id="customizing-operations">Customizing Operations</h3>
        <p>Take a common usage of <code>useQuery</code> from Tanstack Query.</p>
        <pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> FIVE_MINUTES = <span class="hljs-number">5000</span> * <span class="hljs-number">60</span>

<span class="hljs-keyword">const</span> usePost = (postId: number) =&gt; {
  <span class="hljs-keyword">return</span> useQuery({
    queryKey: [<span class="hljs-string">"post"</span>, postId],
    queryFn: <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> fetchPost(postId),
    retry: <span class="hljs-number">3</span>,
    staleTime: FIVE_MINUTES
  })
}
        </code></pre>
        <p>
          The problem is that we customize everything through a bag of
          configuration properties! This bag of properties is solely maintained
          by Tanstack Query, and adding a new property to this bag requires the
          approval of the maintainers. In essence, this makes adding custom
          higher order behavior to your queries nigh-impossible.
        </p>
        <p>Compare this to how queries are customized in Swift Operation.</p>
        <pre><code class="lang-swift">
struct <span class="hljs-string">PostQuery:</span> QueryRequest {
  let <span class="hljs-string">postId:</span> Int

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

let fiveMinutes = TimeInterval(<span class="hljs-number">60</span> * <span class="hljs-number">5</span>)

let query = PostQuery(<span class="hljs-string">postId:</span> <span class="hljs-number">10</span>)
  .retry(<span class="hljs-string">limit:</span> <span class="hljs-number">3</span>)
  .stale(<span class="hljs-string">after:</span> fiveMinutes)
        </code></pre>
        <p>
          The customization very much feels like customizing a SwiftUI view with
          a bunch of view modifiers. In fact, you can create your own modifiers
          with the <code>OperationModifier</code> protocol. Let’s look at how
          you can create a modifier to add artificial delay to any operation,
          whether or not that’s a mutation, query, or paginated request.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">import</span> Dependencies
<span class="hljs-keyword">import</span> Operation

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">OperationRequest</span> </span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">previewDelay</span><span class="hljs-params">(
    shouldDisable: Bool = <span class="hljs-literal">false</span>,
    <span class="hljs-number">_</span> delay: Duration? = <span class="hljs-literal">nil</span>
  )</span></span> -&gt; <span class="hljs-type">ModifiedOperation</span>&lt;<span class="hljs-type">Self</span>, _PreviewDelayModifier&lt;<span class="hljs-type">Self</span>&gt;&gt; {
    <span class="hljs-keyword">self</span>.modifier(_PreviewDelayModifier(shouldDisable: shouldDisable, delay: delay))
  }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">_PreviewDelayModifier</span>&lt;<span class="hljs-title">Operation</span>: <span class="hljs-title">OperationRequest</span>&gt;: <span class="hljs-title">OperationModifier</span>, <span class="hljs-title">Sendable</span> </span>{
  <span class="hljs-keyword">let</span> shouldDisable: <span class="hljs-type">Bool</span>
  <span class="hljs-keyword">let</span> delay: <span class="hljs-type">Duration</span>?

  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setup</span><span class="hljs-params">(context: <span class="hljs-keyword">inout</span> OperationContext, using query: Operation)</span></span> {
    context[<span class="hljs-type">DisablePreviewDelayKey</span>.<span class="hljs-keyword">self</span>] = <span class="hljs-keyword">self</span>.shouldDisable
    query.setup(context: &amp;context)
  }

  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">run</span><span class="hljs-params">(
    isolation: isolated <span class="hljs-params">(any Actor)</span></span></span>?,
    <span class="hljs-keyword">in</span> context: <span class="hljs-type">OperationContext</span>,
    using query: <span class="hljs-type">Operation</span>,
    with continuation: <span class="hljs-type">OperationContinuation</span>&lt;<span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span>, <span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>&gt;
  ) async <span class="hljs-keyword">throws</span>(<span class="hljs-type">Operation</span>.<span class="hljs-type">Failure</span>) -&gt; <span class="hljs-type">Operation</span>.<span class="hljs-type">Value</span> {
    @<span class="hljs-type">Dependency</span>(\.context) <span class="hljs-keyword">var</span> mode
    <span class="hljs-keyword">guard</span> mode == .preview &amp;&amp; !context[<span class="hljs-type">DisablePreviewDelayKey</span>.<span class="hljs-keyword">self</span>] <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await query.run(isolation: isolation, <span class="hljs-keyword">in</span>: context, with: continuation)
    }
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> delay {
      <span class="hljs-keyword">try</span>? await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: delay)
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">try</span>? await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-type">Double</span>.random(<span class="hljs-keyword">in</span>: <span class="hljs-number">0.1</span>...<span class="hljs-number">3</span>)))
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await query.run(isolation: isolation, <span class="hljs-keyword">in</span>: context, with: continuation)
  }
}

<span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">DisablePreviewDelayKey</span>: <span class="hljs-title">OperationContext</span>.<span class="hljs-title">Key</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> defaultValue = <span class="hljs-literal">false</span>
}
        </code></pre>
        <p>
          This modifier is used in the
          <a href="https://github.com/mhayes853/swift-operation/tree/main/Examples/CanIClimb">demo app</a>
          that ships with the library to add fake random delay to every single
          operation. This allows me to preview the experience that an actual
          user with a flakey internet connection would have, whilst still
          mocking fake results for the UI.
        </p>
        <p>
          Another
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CanIClimb/CanIClimbKit/Sources/CanIClimbKit/OperationDevToolsCore/OperationAnalysis.swift">modifier</a>
          is used to also power dev tools that analyze the run time of every
          single operation in the app.
        </p>
        <img class="dev-tools-image" src="./swift-operation/assets/dev-tools-1.jpeg" alt="Dev tools showing the results of many operations displayed on a single scrollable list.">
        <p>
          To handle automatically displaying alerts when an operation succeeds
          or fails, I also created another
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CanIClimb/CanIClimbKit/Sources/CanIClimbKit/SharedHelpers/AlertStateQuery.swift">modifier</a>
          that utilizes <code>AlertState</code> from
          <a href="https://github.com/pointfreeco/swift-navigation">￼<code>swift-navigation</code>￼</a>. This modifier was often used in conjunction with mutations.
        </p>
        <p>
          Custom modifiers allow for higher-order functionality that you can’t
          get in Tanstack Query, and I’ve found this kind of configuration
          flexibility to even be useful in practical scenarios as can be seen by
          the above modifiers.
        </p>
        <h3 id="operation-types">Operation Types</h3>
        <p>
          Tanstack Query gives you queries, mutations, and infinite queries. All
          of these operation types are baked into the library, and new ones
          cannot be added directly.
        </p>
        <p>
          Swift Operation also gives you those 3 operation types, though
          infinite queries are called paginated queries, but you can create more
          kinds if you want. Queries, mutations, and paginated queries all
          inherit from the <code>StatefulOperationRequest</code> protocol, and
          <code>StatefulOperationRequest</code> inherits from the
          <code>OperationRequest</code> protocol. This generality means that you
          can write a custom operation modifier, and it will automatically work
          for queries, mutations, paginated queries, and any other custom
          operation types you come up with all at the same time.
        </p>
        <p>
          There’s an article in the documentation that describes the basics of
          creating a custom operation type from scratch. I will note that
          creating an operation type with the same robustness as the built-in
          types would take a lot of effort, but generally I think the need to
          create a fully robust operation type is quite a rarity. I’ve only
          found myself creating a custom operation type on 1 occasion so far,
          which was to create a type that could paginate many different data
          streams at once for a very specific use-case. In that case, it was
          enough to create a custom <code>OperationState</code> concrete type,
          so I think the bigger idea is that you can modularly replace parts of
          the built-ins as needed.
        </p>
        <p>
          Additionally, it’s also possible to create operation types that don’t
          require any state management because <code>OperationRequest</code> has
          no stateful requirements.
        </p>
        <h3 id="multiple-data-updates">Multiple Data Updates</h3>
        <p>
          Another benefit of Swift Operation is that it’s possible to yield
          multiple data updates from an operation while it’s still running
          through the <code>OperationContinuation</code> type that’s handed to
          each operation run. Tanstack Query also has some support for this
          through
          <a href="https://github.com/TanStack/query/blob/main/packages/query-core/src/streamedQuery.ts">￼<code>streamedQuery</code>￼</a>
          which works via
          <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of">async iterators</a>
          (think JavaScript’s version of <code>AsyncSequence</code>), but the
          issue with that API is that any query that uses it will not retain its
          loading state after the first chunk of data arrives.
        </p>
        <p>
          Regardless, in today’s LLM-based application landscape, streaming data
          is far more relevant than it once was. Therefore, I wanted to make
          this a first-class citizen in Swift Operation. The
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CanIClimb/CanIClimbKit/Sources/CanIClimbKit/MountainsCore/ClimbReadiness/Mountain+ClimbReadinessGeneration.swift">demo app</a>
          shows how you can use Apple’s new FoundationModels in conjunction with
          <code>OperationContinuation</code> to stream data while it arrives
          from the underlying on-device model.
        </p>
        <p>
          Another use case for multiple data updates would be yielding data
          that’s locally stored on disk whilst fetching the freshest data from a
          remote server. In fact, this can be a great way to implement offline
          support as shown many
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CanIClimb/CanIClimbKit/Sources/CanIClimbKit/MountainsCore/Mountain+Detail.swift#L61">different times</a>
          in the demo app.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">struct</span> MyQuery: QueryRequest {
  <span class="hljs-comment">// ...</span>

  <span class="hljs-function">func <span class="hljs-title">fetch</span>(<span class="hljs-params">
    isolation: isolated (any Actor</span>)?,
    <span class="hljs-keyword">in</span> context: OperationContext,
    with continuation: OperationContinuation&lt;Value, any Error&gt;
  ) <span class="hljs-keyword">async</span> throws -&gt; Value </span>{
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">let</span> <span class="hljs-keyword">value</span> = serverValue()
    continuation.<span class="hljs-keyword">yield</span>(<span class="hljs-function"><span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> <span class="hljs-title">locallyPersistedValue</span>(<span class="hljs-params"></span>))
    return <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> <span class="hljs-keyword">value</span>
  }
}</span>
        </code></pre>
        <p>
          Tanstack Query also supports the above use case, but I think the main
          difference between the 2 libraries is that Swift Operation surfaces
          this functionality in a more direct way.
        </p>
        <h3 id="detached-state-management">Detached State Management</h3>
        <p>
          In Tanstack Query all queries and mutations are managed directly by
          <code>QueryClient</code>, and as a result an instance of the client is
          needed everywhere to observe the state of a query or mutation.
        </p>
        <p>
          This is also the default in Swift Operation (except
          <code>QueryClient</code> is named <code>OperationClient</code>), with
          the key difference being that the <code>OperationStore</code> type is
          solely responsible for the state management of an individual
          operation. The <code>OperationClient</code> merely just holds all the
          store instances created by your app, and allows you to query specific
          store instances using an <code>OperationPath</code> (the equivalent of
          the query key in Tanstack Query). However, it’s also possible to
          create stores that are not stored inside an
          <code>OperationClient</code>, and these are called
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Sources/OperationCore/Store/OperationStore.swift#L151">“detached stores”</a>.
        </p>
        <p>Detached stores are particularly useful for 3 cases.</p>
        <ol>
          <li>
            You want multiple and completely separate store instances for the
            same operation.
          </li>
          <li>
            You don’t want the store to be deallocated as a result of being
            evicted from the store cache within an <code>OperationClient</code>.
            <ol>
              <li>
                By default, this happens when your application runs low on
                memory, but it’s also possible to create your own custom store
                cache that uses a different eviction scheme (LRU, garbage
                collection, etc.).
              </li>
            </ol>
          </li>
          <li>
            You don’t like how <code>OperationClient</code> manages its pool of
            stores, and you want to create your own pool for some reason.
            <ol>
              <li>
                This last point is a testament to one of the design principles
                of the library. If you don’t like a built-in API, then you
                should have the power to create and use your own implementation
                of that API with the library.
              </li>
            </ol>
          </li>
        </ol>
        <h3 id="run-specifications">Run Specifications</h3>
        <p>
          Tanstack Query has built-in support for refetching queries when the
          application re-enters the foreground from the background (Tanstack
          calls this refetching on focus), and refetching queries when the
          network status flips from offline to online. This is particularly
          useful for keeping data as fresh as possible in your UI in the context
          of the user switching between apps, or when implementing a
          “Reconnecting…” dialog.
        </p>
        <p>
          Swift Operation also supports automatically rerunning operations in
          these circumstances, but yet again it does so in a modular way. While
          Tanstack Query bakes this kind of refetching directly into a query,
          Swift Operation builds this functionality generically on-top of the
          <code>OperationRunSpecification</code> protocol.
        </p>
        <p>
          A run specification allows you to control when an operation
          automatically reruns. It has 2 requirements.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-selector-tag">public</span> <span class="hljs-selector-tag">protocol</span> <span class="hljs-selector-tag">OperationRunSpecification</span> {
  <span class="hljs-comment">/// Returns whether or not this condition is satisfied in the specified context.</span>
  <span class="hljs-selector-tag">func</span> <span class="hljs-selector-tag">isSatisfied</span>(in <span class="hljs-attribute">context</span>: OperationContext) <span class="hljs-selector-tag">-</span>&gt; <span class="hljs-selector-tag">Bool</span>

  <span class="hljs-comment">/// Subcribes to changes on this specification in the specified context.</span>
  <span class="hljs-selector-tag">func</span> <span class="hljs-selector-tag">subscribe</span>(
    in <span class="hljs-attribute">context</span>: OperationContext,
    <span class="hljs-attribute">onChange</span>: <span class="hljs-variable">@escaping</span> <span class="hljs-variable">@Sendable</span> () -&gt; Void
  ) <span class="hljs-selector-tag">-</span>&gt; <span class="hljs-selector-tag">OperationSubscription</span>
}
        </code></pre>
        <p>
          <code>isSatisfied</code> is general invoked immediately after
          <code>onChange</code> is called within <code>subscribe</code>.
        </p>
        <p>
          The
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Sources/OperationCore/RunSpecifications/Modifiers/RerunOnChangeOperation.swift">￼<code>rerunOnChange</code>￼ modifier</a>
          provides the same operation rerunning capabilities as Tanstack Query’s
          automatic refetching. The modifier takes a run specification, and will
          automatically rerun the operation it modifies whenever it detects that
          the return value of <code>isSatisfied</code> changes to true. In fact,
          the actual refetching is further built on top of the generic
          <code>OperationController</code> protocol.
        </p>
        <p>
          <code>NetworkConnectionRunSpecification</code> and
          <code>ApplicationIsActiveRunSpecification</code> are the run
          specifications that power the same refetching abilities that you would
          find in Tanstack Query. The difference between the 2 libraries in this
          regard, is that you can also create
          <a href="https://github.com/mhayes853/swift-operation/blob/main/Examples/CaseStudies/CaseStudies/03-CustomFetchConditions.swift">custom run specifications</a>
          to drive automatic rerunning.
        </p>
        <h3 id="stateless-operations">Stateless Operations</h3>
        <p>
          Tanstack Query solely advertises itself as an asynchronous state
          manager, yet many of its features, like retries, also work in a
          stateless operation paradigm.
        </p>
        <p>
          What do I mean by stateless operation? A stateless operation is an
          operation that doesn’t need to have its state tracked by a UI or
          something else in real-time. Just think of it as a typical
          <code>async</code> function where you also don’t need to track whether
          or not the operation is in a loading, error, or success state.
        </p>
        <p>
          Since Tanstack Query is so focused on state management with its API,
          it only really makes sense to use it in your UI layer. If you have
          async operations that run in background or non-UI contexts, you
          generally don’t use Tanstack Query even if you would benefit from some
          of its features (eg. Deduplication and retries).
        </p>
        <p>
          I wanted to make Swift Operation something that is also viable in your
          data layer, and other non-UI contexts such as within tools in the
          FoundationModels framework. This is why there’s both an
          <code>OperationRequest</code> and
          <code>StatefulOperationRequest</code> protocol, where
          <code>StatefulOperationRequest</code> inherits from
          <code>OperationRequest</code>.
        </p>
        <p>
          If you create a direct conformances to <code>OperationRequest</code>,
          you aren’t required to specify a <code>State</code> associated type,
          or an <code>OperationPath</code> that uniquely identifies your
          operation. In effect, conforming to
          <code>OperationRequest</code> directly signals to the library that
          your operation is stateless, or that it’s just an async function that
          performs some work and returns a result.
        </p>
        <p>
          Of course, you cannot create <code>OperationStore</code> instances
          with stateless operations, so you will need
          <code>StatefulOperationRequest</code> to be compatible with the store.
          However, you still get access to a wealth of modifiers with stateless
          operations, including the ability to create modifiers that change or
          enhance the output type of your operation. Stateful operations cannot
          use modifiers that change their output type because their
          <code>State</code> type only works with a certain output type.
        </p>
        <br>
        <h2 id="future-directions">Future Directions</h2>
        <p>
          I think I’ve packed a lot into the <code>0.1.0</code> release of the
          library, but I have many more ideas. This list is merely a brain dump
          of whatever I can think of at the moment.
        </p>
        <ol>
          <li>
            Embedded Swift Support
            <ol>
              <li>
                The library already supports all major Swift platforms
                (including Android, Windows, and WASM), but the main thing
                holding back Embedded support IMO would be the use of
                existentials in deep corners of the library (eg. Within
                <code>OperationContext</code>).
              </li>
            </ol>
          </li>
          <li>
            Dev Tools
            <ol>
              <li>
                The demo app shows how basic dev tools can be made that allow
                you to analyze every single operation run that your app
                performs. However, it would be better to have a library specific
                solution to this that’s cross platform, but this will probably
                take a lot of time and planning.
              </li>
            </ol>
          </li>
          <li>
            Better Defaults on Non-Apple Platforms
            <ol>
              <li>
                For instance, the default store cache on non-Apple platforms
                does not use any mechanism to evict store entries because
                <code>DistpatchSource.makeMemoryPressureSource</code> isn’t
                available on those platforms.
              </li>
              <li>
                Additionally, Linux, Windows, and Android do not have a default
                <code>NetworkObserver</code> and
                <code>ApplicationActivityObserver</code>. WASM (in web browser
                applications) has dedicated default implementations for those 2
                protocols if you enable the
                <code>SwiftOperationWebBrowser</code> trait.
              </li>
            </ol>
          </li>
          <li>
            Macros
            <ol>
              <li>
                At the moment, the library ships with no macros. However, it’s
                also quite verbose with respect to defining operations. If there
                could be a way to make creating operations just as simple as
                defining normal functions (eg. The
                <code>@DatabaseFunction</code> macro from
                <a href="https://github.com/pointfreeco/swift-structured-queries">Structured Queries</a>), that would be nice. I just haven’t figured out how this
                would fully work with operation types with multiple requirements
                such as <code>PaginatedRequest</code>.
              </li>
            </ol>
          </li>
          <li>
            A Better Way to Derive Dependent Operations
            <ol>
              <li>
                By this, I mean constructing operations that depend on the
                results of other operations. There is a documentation article
                that describes how to do this, but the solution is far from
                ideal, and I’m not at all satisfied with it.
              </li>
            </ol>
          </li>
        </ol>
        <br>
        <h2 id="try-it-today">Try it Today</h2>
        <p>
          I think the library is ready to be used on a larger scale now, and I
          don’t think I can keep improving it by iterating in isolation for much
          longer. That being said, it’s far from mature, so I would advise
          against using it in enterprise scale or mission-critical apps at the
          time of writing this.
        </p>
        <p>
          The
          <a href="https://github.com/mhayes853/swift-operation">repo</a> has a
          lot more material describing how to use the library in different
          contexts, so check it out!
        </p>]]></content:encoded>
    </item>
    <item>
      <title>On Swift Package Traits</title>
      <link>https://whypeople.xyz/on-package-traits</link>
      <guid isPermaLink="true">https://whypeople.xyz/on-package-traits</guid>
      <pubDate>Fri, 08 Aug 2025 00:00:00 GMT</pubDate>
      <description>What are package traits, and how I used this lesser known SPM feature in swift-uuidv7.</description>
      <content:encoded><![CDATA[<h1 id="on-swift-package-traits" class="title">
          On Swift Package Traits
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          I recently released a
          <a href="https://github.com/mhayes853/swift-uuidv7">small little library</a>
          for generating version 7 UUIDs in Swift that integrates with popular
          libraries such as GRDB, Structured Queries, Tagged, and more. Some of
          the interesting comments I’ve gotten relate to one of SPM’s relatively
          new features (introduced in Swift 6.1) called <em>package traits</em>.
          From what it seems, this new SPM feature is relatively unknown, yet I
          think many packages would benefit from utilizing this feature to
          support integrations with other packages.
        </p>
        <p>
          If you’ve written Rust before, you may be familiar with
          <a href="https://doc.rust-lang.org/cargo/reference/features.html">cargo features</a>. Package traits are essentially the equivalent of cargo features,
          but for Swift.
        </p>
        <p>
          Though, I’m guessing that most people reading this are unfamiliar with
          Rust, so this article should give enough of an overview of traits.
          Alternatively, you can read the
          <a href="https://github.com/swiftlang/swift-evolution/blob/main/proposals/0450-swiftpm-package-traits.md">proposal</a>
          for package traits, but that won’t tell you about how I used them in
          <code>swift-uuidv7</code>.
        </p>
        <br>
        <h2 id="what-are-package-traits-">What are Package Traits?</h2>
        <p>Great question!</p>
        <p>
          Package traits allow you to selectively enable functionality on a
          swift package. As a package author, you declare them in your
          <code>Package.swift</code> file like so.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// swift-tools-version: 6.1</span>
<span class="hljs-comment">// The swift-tools-version declares the minimum version of Swift required to build this package.</span>

import <span class="hljs-type">PackageDescription</span>

<span class="hljs-keyword">let</span> package = <span class="hljs-type">Package</span>(
  name: <span class="hljs-string">"swift-uuidv7"</span>,
  platforms: [.iOS(.v13), .macOS(.v10_15), .tvOS(.v13), .watchOS(.v7), .macCatalyst(.v13)],
  products: [.library(name: <span class="hljs-string">"UUIDV7"</span>, targets: [<span class="hljs-string">"UUIDV7"</span>])],
  traits: [
    .<span class="hljs-keyword">trait</span>(
      name: <span class="hljs-string">"SwiftUUIDV7Tagged"</span>,
      description: <span class="hljs-string">"Adds integrated swift-tagged support to the UUIDV7 type."</span>
    ),
    .<span class="hljs-keyword">trait</span>(
      name: <span class="hljs-string">"SwiftUUIDV7StructuredQueries"</span>,
      description:
        <span class="hljs-string">"Adds swift-structured-queries support and column representations to the UUIDV7 type."</span>
    ),
    .<span class="hljs-keyword">trait</span>(
      name: <span class="hljs-string">"SwiftUUIDV7GRDB"</span>,
      description: <span class="hljs-string">"""
        Conforms UUIDV7 to GRDB's DatabaseValueConvertible and StatementColumnConvertible \
        protocols, and adds database functions to generate, parse, and extract data from UUIDV7s.
        """</span>
    ),
    .<span class="hljs-keyword">trait</span>(
      name: <span class="hljs-string">"SwiftUUIDV7Dependencies"</span>,
      description:
        <span class="hljs-string">"""
        Adds a dependency value to generate UUIDV7s, and interops the base UUID dependency with \
        UUIDV7 generation.
        """</span>
    )
  ],
  <span class="hljs-comment">// ...</span>
)
        </code></pre>
        <p>
          Then, inside your package, you can selectively compile bits of your
          code by using them as a flag within compiler directive.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// If SwiftUUIDV7StructuredQueries is enabled, then UUIDV7 conforms to</span>
<span class="hljs-comment">// QueryBindable from StructuredQueries.</span>

#<span class="hljs-keyword">if</span> <span class="hljs-type">SwiftUUIDV7StructuredQueries</span>
  <span class="hljs-keyword">import</span> Foundation
  <span class="hljs-keyword">import</span> StructuredQueriesCore

  <span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">UUIDV7</span>: <span class="hljs-title">QueryBindable</span> </span>{}

  <span class="hljs-comment">// ...</span>
#endif
        </code></pre>
        <h3 id="selective-dependency-compilation">
          Selective Dependency Compilation
        </h3>
        <p>
          You can also choose to only selectively compile package dependencies
          when a specific trait is enabled. This works similarly to how you can
          tell a Swift Package to selectively compile a dependency for a
          specific platform. In other words, as a package author, your
          <code>Package.swift</code> will look like this.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// swift-tools-version: 6.1</span>
<span class="hljs-comment">// The swift-tools-version declares the minimum version of Swift required to build this package.</span>

import PackageDescription

let package = Package(
  <span class="hljs-comment">// ...</span>
<span class="hljs-symbol">  targets:</span> [
    .target(
<span class="hljs-symbol">      name:</span> <span class="hljs-string">"UUIDV7"</span>,
<span class="hljs-symbol">      dependencies:</span> [
        <span class="hljs-comment">// ...</span>
        .product(
<span class="hljs-symbol">          name:</span> <span class="hljs-string">"StructuredQueriesCore"</span>,
<span class="hljs-symbol">          package:</span> <span class="hljs-string">"swift-structured-queries"</span>,
<span class="hljs-symbol">          condition:</span> .when(traits: [<span class="hljs-string">"SwiftUUIDV7StructuredQueries"</span>])
        ),
        <span class="hljs-comment">// ...</span>
      ]
    ),
    .testTarget(name: <span class="hljs-string">"UUIDV7Tests"</span>, dependencies: [<span class="hljs-string">"UUIDV7"</span>])
  ]
)
        </code></pre>
        <p>
          In this example, <code>StructuredQueriesCore</code> is only compiled
          when the <code>SwiftUUIDV7StructuredQueries</code> trait is enabled.
          This feature is how <code>swift-uuidv7</code> is able to integrate
          with many different large libraries whilst still keeping the build
          time as minimal as possible.
        </p>
        <h3 id="dependent-traits">Dependent Traits</h3>
        <p>
          You may have traits that depend on other traits being enabled. For
          instance, there’s a branch in <code>swift-uuidv7</code> that adds a
          new <code>SwiftUUIDV7SharingGRDB</code> trait that will be merged once
          <a href="https://www.pointfree.co/blog/posts/181-a-swiftdata-alternative-with-sqlite-cloudkit-public-beta">CloudKit support</a>
          for SharingGRDB is publicly released.
        </p>
        <p>
          SharingGRDB depends both on StructuredQueries and GRDB, so enabling
          <code>SwiftUUIDV7SharingGRDB</code> trait should also enable
          <code>SwiftUUIDV7StructuredQueries</code> and
          <code>SwiftUUIDV7GRDB</code>. This is accomplished like so.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// swift-tools-version: 6.1</span>
<span class="hljs-comment">// The swift-tools-version declares the minimum version of Swift required to build this package.</span>

<span class="hljs-keyword">import</span> PackageDescription

<span class="hljs-keyword">let</span> <span class="hljs-keyword">package</span> = Package(
  <span class="hljs-comment">// ...,</span>
  traits: [
    <span class="hljs-comment">// ...</span>
    .trait(
      name: <span class="hljs-string">"SwiftUUIDV7SharingGRDB"</span>,
      description: <span class="hljs-string">"""
        Conforms UUIDV7 to IdentifierStringConvertible to make it compatible with CloudKit sync.

        This trait also enables SwiftUUIDV7GRDB and SwiftUUIDV7StructuredQueries.
        """</span>,
      enabledTraits: [<span class="hljs-string">"SwiftUUIDV7GRDB"</span>, <span class="hljs-string">"SwiftUUIDV7StructuredQueries"</span>]
    )
  ],
  <span class="hljs-comment">// ...</span>
)
        </code></pre>
        <h3 id="default-traits">Default Traits</h3>
        <p>
          You can also choose to enable some traits by default on a package.
          <code>swift-uuidv7</code> has no need for this because all of its
          traits conditionally compile external dependencies. If the user has no
          need for those external dependencies, and just wants the base
          <code>UUIDV7</code> type, then we shouldn’t compile them.
        </p>
        <p>
          Regardless, default traits are enabled like so (this is a simplified
          example taken from
          <a href="https://github.com/swiftlang/swift-subprocess"><code>swift-subprocess</code></a>).
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">let</span> <span class="hljs-keyword">package</span> = Package(
  <span class="hljs-comment">// ...</span>
  traits: [
    <span class="hljs-string">"SubprocessFoundation"</span>,
    <span class="hljs-string">"SubprocessSpan"</span>,
    .<span class="hljs-keyword">default</span>(enabledTraits: [<span class="hljs-string">"SubprocessFoundation"</span>, <span class="hljs-string">"SubprocessSpan"</span>])
  ],
  <span class="hljs-comment">// ...</span>
)
        </code></pre>
        <h3 id="building-with-traits-enabled">Building With Traits Enabled</h3>
        <p>
          When building your package, you can specify which traits you want to
          build the package with like so.
        </p>
        <pre><code class="lang-sh"><span class="hljs-keyword">swift </span><span class="hljs-keyword">build </span>--traits <span class="hljs-keyword">SwiftUUIDV7Tagged</span></code></pre>
        <p>You can also enable all traits like so.</p>
        <pre><code class="lang-sh"><span class="hljs-keyword">swift </span><span class="hljs-keyword">build </span>--enable-all-traits</code></pre>
        <br>
        <h2 id="how-do-i-use-package-traits-">How do I use package traits?</h2>
        <p>
          We’ve gone over how to add package traits to a package, so now let’s
          go over how you can use them.
        </p>
        <p>
          <strong><em>Unfortunately, at the time of writing this, Xcode’s UI does not
              support enabling traits on a Swift Package. Therefore, you cannot
              use package traits in a traditional Xcode project.</em></strong>
        </p>
        <p>
          Instead, you must enable them in your own packages through your
          <code>Package.swift</code> file. Here’s how that is done.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// swift-tools-version: 6.2</span>
<span class="hljs-comment">// The swift-tools-version declares the minimum version of Swift required to build this package.</span>

<span class="hljs-keyword">import</span> PackageDescription

let <span class="hljs-keyword">package</span> = <span class="hljs-keyword">Package</span>(
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">dependencies</span>: [
    <span class="hljs-comment">// ...</span>
    .<span class="hljs-keyword">package</span>(
      url: <span class="hljs-string">"https://github.com/mhayes853/swift-uuidv7"</span>,
      branch: <span class="hljs-string">"sharing-grdb-icloud"</span>,
      traits: [<span class="hljs-string">"SwiftUUIDV7SharingGRDB"</span>, <span class="hljs-string">"SwiftUUIDV7Tagged"</span>, <span class="hljs-string">"SwiftUUIDV7Dependencies"</span>]
    )
  ],
)
        </code></pre>
        <p>That’s all, you can now begin using them right away!</p>
        <p>
          However, passing an explicit set of traits will disable the default
          trait. If you still want to enable the default trait, you must also
          pass <code>.default</code> to the set of traits.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// swift-tools-version: 6.2</span>
<span class="hljs-comment">// The swift-tools-version declares the minimum version of Swift required to build this package.</span>

<span class="hljs-keyword">import</span> PackageDescription

let <span class="hljs-keyword">package</span> = <span class="hljs-keyword">Package</span>(
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">dependencies</span>: [
    <span class="hljs-comment">// ...</span>
    .<span class="hljs-keyword">package</span>(
      url: <span class="hljs-string">"https://github.com/swiftlang/swift-subprocess.git"</span>,
      branch: <span class="hljs-string">"main"</span>,

      <span class="hljs-comment">// Keeps the default trait enabled despite passing an explicit set of traits.</span>
      traits: [.default]
    )
  ],
)
        </code></pre>
        <br>
        <h2 id="using-traits-to-integrate-with-other-packages">
          Using Traits to Integrate with Other Packages
        </h2>
        <p>
          On its own, <code>swift-uuidv7</code> provides a simple
          <code>UUIDV7</code> type that’s more robust than other Swift
          implementations in the wild by offering timestamp extraction, a
          Comparable conformance, and sub-millisecond monotonicity. Yet almost
          certainly you’ll want to store instances of <code>UUIDV7</code> in
          your database using GRDB and Structured Queries, use them as entity
          identifiers for App Intents, and much more.
        </p>
        <p>
          You can certainly create the very trivial
          <code>@retroactive</code> conformances on <code>UUIDV7</code> in your
          own project. For instance.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// Inside your project...</span>

<span class="hljs-keyword">import</span> UUIDV7
<span class="hljs-keyword">import</span> StructuredQueries

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">UUIDV7</span>: @<span class="hljs-title">retroactive</span> <span class="hljs-title">QueryBindable</span> </span>{}
        </code></pre>
        <p>
          However, this conformance would only live inside your own project.
          Either you would have to duplicate it between different projects, or
          you would have to create a dedicated package for this 1 line
          conformance. For many, copying it between different projects is
          probably fine, but it still isn’t the most ideal solution here. The
          latter option of creating a tiny package at scale would probably
          create the same micro-package culture that can be found in the NPM
          ecosystem.
        </p>
        <p>
          Rather, we can use package traits to solve this issue, and provide an
          official conformance that can be used in any project.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// Inside swift-uuidv7...</span>

#<span class="hljs-keyword">if</span> <span class="hljs-type">SwiftUUIDV7StructuredQueries</span>
  <span class="hljs-keyword">import</span> Foundation
  <span class="hljs-keyword">import</span> StructuredQueriesCore

  <span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">UUIDV7</span>: <span class="hljs-title">QueryBindable</span> </span>{}
#endif
        </code></pre>
        <h3 id="who-adds-the-trait-">Who adds the trait?</h3>
        <p>
          In the above example, either <code>StructuredQueries</code> or
          <code>UUIDV7</code> can add a trait to support this integration.
        </p>
        <p>
          For instance, <code>StructuredQueries</code> could create a
          <code>StructuredQueriesUUIDV7</code> trait that adds a dependency on
          <code>swift-uuidv7</code>, and that trait would provide the
          conformance that you see above. Alternatively,
          <code>UUIDV7</code> could create a
          <code>SwiftUUIDV7StructuredQueries</code> trait that adds a dependency
          on <code>swift-structured-queries</code>, and that trait would provide
          the conformance you see above. Obviously, only 1 package can actually
          make such a trait because we would be in circular hell otherwise.
        </p>
        <p>
          Since <code>UUIDV7</code> is designed to integrate with numerous
          different libraries (5 at the time of writing this), it would be quite
          cumbersome if I had to convince the maintainers of each library to add
          a trait specifically for my very niche package. Therefore, it’s for
          the best if I provided all the integrations myself through
          <code>UUIDV7</code>, and in return it keeps all the integrations
          bundled in 1 simple place.
        </p>
        <h3 id="a-coordination-problem">A Coordination Problem</h3>
        <p>
          That being said, this can turn into a nasty coordination problem if
          there isn’t any consideration around which packages add certain
          traits.
        </p>
        <p>
          For instance, what if a hot new library comes around and adds
          <code>UUIDV7</code> support through a trait of its own? Now the
          simplicity of having all of <code>UUIDV7</code>s integrations in a
          single place is lost, because a new library now maintains its own
          integration with <code>UUIDV7</code>.
        </p>
        <p>
          We could define best practices that state that smaller and more stable
          packages like <code>UUIDV7</code> should not offer any package
          integration traits, and instead only let other more volatile and
          larger packages (eg. GRDB, Structured Queries) define such traits.
          From the standpoint of how I
          <a href="personal-laws-of-design#objects-are-best-used-as-agents-of-knowledge">think of objects</a>, this makes a lot of sense because larger packages with more
          knowledge give meaning to the smaller and simpler packages with less
          knowledge.
        </p>
        <p>
          However, this now creates a problem where maintainers of more volatile
          and larger packages have to specifically set out to implement their
          own integration of such smaller packages. On one hand, this can be a
          maintenance burden if those maintainers decide to integrate with a lot
          of smaller packages. On the other hand, it also means that
          integrations with smaller packages are not readily available because
          it would require the efforts of several large package maintainers,
          each with their own more important concerns.
        </p>
        <p>
          If we want the best outcome, I don’t think we can rely on an entirely
          dogmatic standard. Rather, this may need to be solved on a package by
          package basis, where each package defines how it integrates with
          others. Of course, the definition of integration will likely change as
          the package evolves. For instance, I don’t think directly supporting
          300 package integrations in <code>UUIDV7</code> is necessarily the
          best maintenance idea, and at that point I would rather delegate those
          integrations to those package maintainers.
        </p>
        <p>
          The larger community would of course have to adhere to those socially
          agreed upon practices for each package, and be informed when those
          practices change. Seeing how this will scale may be quite interesting.
        </p>
        <p>— 8/8/25</p>
        <br>
        <hr>
        <br>
        <p>
          PS. I’m creating a new series of content that I will call “Swift Bits”
          that will contain small Swift-specific articles such as these.
        </p>
        <p>
          I mainly want to focus my writing more on higher level ideas that
          apply to building products and creating new mediums of spreading
          ideas. Those ideas carry over into how I write Swift on a day-to-day
          basis, but I think only focusing on Swift as a whole is quite a narrow
          viewpoint on designing real systems.
        </p>
        <p>
          Still, seeing language specifics directly can be useful given the
          detachment most programming languages have from the systems they
          create. In that sense, I do believe that this kind of writing is
          worthwhile from time to time.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>How to Teach Monads in Programming</title>
      <link>https://whypeople.xyz/monads</link>
      <guid isPermaLink="true">https://whypeople.xyz/monads</guid>
      <pubDate>Thu, 17 Jul 2025 00:00:00 GMT</pubDate>
      <description>You don&apos;t learn monads by learning monads.</description>
      <content:encoded><![CDATA[<h1 id="how-to-teach-someone-monads-in-programming" class="title">
          How to Teach Monads in Programming
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          So, how does one understand this seemingly impossible to understand
          topic in programming? If seemingly, no one can explain it to a 5 year
          old, do we truly understand this intellectual monstrosity?
        </p>
        <p>Google what a monad is, and this is one of the top image results.</p>
        <img src="./monads/assets/monad.png" alt="A mathematical drawing of a monad that describes this function: (>>=):: ma → (a→ mb) → mb.">
        <p>
          I’m sure that taught you a lot, and now I’m totally sure that you
          really understand what a monad is…
        </p>
        <p>
          Well, we could also try by showing you an example of one. Here, take a
          look at the
          <a href="https://developer.apple.com/documentation/swift/result">documentation</a>
          for <code>Result</code> in Swift!
        </p>
        <p>
          Still doesn’t make you understand it… Ok, how about we have
          <a href="https://chatgpt.com/share/68796b83-f57c-8009-ab55-ca698d8de980">ChatGPT</a>
          give us a nice summary! (I’ll even tell it that I’m a 5 year old!)
        </p>
        <pre><code>
Okay! Imagine you have <span class="hljs-keyword">a</span> **magic box**. This magic box can hold *<span class="hljs-literal">one</span> toy* inside.

Now, this isn’t just <span class="hljs-keyword">any</span> box—<span class="hljs-keyword">it</span>’s <span class="hljs-keyword">a</span> **special box**:
    <span class="hljs-number">1.</span>    You can **<span class="hljs-built_in">put</span>** <span class="hljs-keyword">a</span> toy <span class="hljs-keyword">in</span> <span class="hljs-keyword">the</span> box.
    <span class="hljs-number">2.</span>    You can **play** <span class="hljs-keyword">with</span> <span class="hljs-keyword">the</span> toy **inside** <span class="hljs-keyword">the</span> box, but only <span class="hljs-keyword">using</span> special gloves (because <span class="hljs-keyword">it</span>’s <span class="hljs-keyword">a</span> magic toy).
    <span class="hljs-number">3.</span>    If <span class="hljs-keyword">the</span> toy does something special (like turning <span class="hljs-keyword">into</span> <span class="hljs-keyword">a</span> different toy), <span class="hljs-keyword">the</span> box makes sure everything stays safe <span class="hljs-keyword">and</span> magical.

That’s kind <span class="hljs-keyword">of</span> what <span class="hljs-keyword">a</span> **monad** is <span class="hljs-keyword">in</span> programming:
* It’s like <span class="hljs-keyword">a</span> box that **holds <span class="hljs-keyword">a</span> <span class="hljs-built_in">value</span>**.
* You can **<span class="hljs-built_in">do</span> things <span class="hljs-keyword">with</span> <span class="hljs-keyword">the</span> <span class="hljs-built_in">value</span>**, but you have <span class="hljs-built_in">to</span> use special rules <span class="hljs-built_in">to</span> keep everything safe <span class="hljs-keyword">and</span> tidy.
* It **knows how <span class="hljs-built_in">to</span> handle <span class="hljs-keyword">the</span> magic stuff** like errors, waiting, <span class="hljs-keyword">or</span> doing things <span class="hljs-keyword">in</span> order.

So, <span class="hljs-keyword">a</span> monad is <span class="hljs-keyword">a</span> *magical box* that helps programmers **play nicely <span class="hljs-keyword">with</span> tricky things** like missing data, <span class="hljs-built_in">time</span> delays, <span class="hljs-keyword">or</span> big sequences <span class="hljs-keyword">of</span> steps—<span class="hljs-keyword">without</span> breaking <span class="hljs-keyword">the</span> game. 🧙📦✨

Want <span class="hljs-keyword">a</span> grown-up <span class="hljs-built_in">version</span> too?
        </code></pre>
        <p>
          ChatGPT has introduced concepts like “special box”, “holds a value”,
          and “handles the magic stuff”… I think you’re probably more confused
          now…
        </p>
        <br>
        <h2 id="good-teaching-and-good-learning-are-experiential">
          Good Teaching and Good Learning are Experiential
        </h2>
        <p>
          This is one of my
          <a href="https://whypeople.xyz/personal-laws-of-design">personal laws of design</a>, and the core idea is that we learn best through using all of our
          human senses (not just hearing and eyesight). Good education is not
          just about transferring information from point A to point B, because
          good education forces you to accept a style of action into your
          lifestyle rather than focusing on concept retention.
        </p>
        <p>
          Therefore, all existing online resources (including this article) will
          never be able to teach you what a monad is in programming. Simply put,
          only reading articles, looking at fancy diagrams, or looking at code
          is not making you adopt the style of thinking that enables you to use
          monads to their fullest potential.
        </p>
        <p>
          <strong><em>You’re failing to learn monads because you’re stuck trying to
              “learn monads”!</em></strong>
        </p>
        <br>
        <h2 id="pain-is-the-only-way-to-learn-monads">
          Pain is the Only Way to Learn Monads
        </h2>
        <p>
          You’re going to learn monads through physical and mental pain. Yes,
          you will experience pain over and over again as you write that same
          repetitive and absolutely annoying code. In fact, you’re not even
          going to be writing monads when you experience this pain, and you
          don’t need to understand them to feel the pain. Eventually, this will
          become so painful that monads will become the natural cure for that
          pain.
        </p>
        <p>
          I can show you examples (in words and code) that describes the
          <em>information</em> of this pain. However, you will only be
          enlightened once you <em>feel</em> that pain yourself.
        </p>
        <h3 id="arrays">Arrays</h3>
        <p>You’ve probably written code like this before.</p>
        <pre><code class="lang-swift">
let<span class="hljs-built_in"> array </span>= [1, 2, 3]
var doubled = [Int]()
for num in<span class="hljs-built_in"> array </span>{
  doubled.append(num * 2)
}
        </code></pre>
        <p>
          Now do this for every single time you want to iterate over an array in
          your application. This is <em>painful</em>. You don’t want to write
          <code>for element in array</code> every single freaking time, and then
          either call <code>array.append</code> or filter through an if
          statement every single time.
        </p>
        <pre><code class="lang-swift">
let array = [1, 2, 3, 4]

var tripled = [<span class="hljs-string">Int</span>](<span class="hljs-link"></span>)
for num in array {
  tripled.append(num * 3)
}

var even = [<span class="hljs-string">Int</span>](<span class="hljs-link"></span>)
for num in tripled {
  guard num.isMultiple(of: 2) else { continue }
  even.append(num)
}

var stringified = [<span class="hljs-string">String</span>](<span class="hljs-link"></span>)
for num in even {
  stringified.append(String(num))
}

var joined = ""
for str in stringified {
  joined += "\(str), "
}

print(joined) // "6, 12, "
        </code></pre>
        <p>
          Reading this code is painful. Writing it was also painful. Good, now
          let’s have monads cure our pain!
        </p>
        <pre><code class="lang-swift">
<span class="hljs-keyword">let</span> str = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]
  .<span class="hljs-keyword">map</span> { $0 * <span class="hljs-number">3</span> }
  .filter { $0.isMultiple(of: <span class="hljs-number">2</span>) }
  .<span class="hljs-keyword">map</span>(String.init)
  .joined(separator: <span class="hljs-string">", "</span>)
        </code></pre>
        <p>
          Much more compact, much less <em>painful</em>. In this case, “the
          magic stuff” (according to the ChatGPT explanation) is the iteration.
          You no longer have to worry about the iteration, and instead you
          specify what the iteration means.
        </p>
        <h3 id="result">Result</h3>
        <p>
          You’re probably familiar with <code>try catch</code> (Swift uses
          <code>do catch</code> instead) for error handling. Sometimes, that can
          be annoying such as when you want to store an <code>Error</code> in a
          struct like so. (An example of where you would want to store an error
          in a struct would be if you wanted to render an error message in your
          UI.)
        </p>
        <pre><code class="lang-swift">
<span class="hljs-comment">// ❌ Bad</span>

<span class="hljs-keyword">struct</span> MyData {
  <span class="hljs-keyword">let</span> <span class="hljs-built_in">error</span>: (any <span class="hljs-built_in">Error</span>)?
  <span class="hljs-keyword">let</span> value: <span class="hljs-keyword">String</span>
}

<span class="hljs-comment">// ✅ Good</span>

<span class="hljs-keyword">struct</span> MyData {
  <span class="hljs-keyword">let</span> result: Result&lt;<span class="hljs-keyword">String</span>, any <span class="hljs-built_in">Error</span>&gt;
}
        </code></pre>
        <p>
          So now let’s say we’ve got a function that throws, and returns a
          <code>Result</code> that doesn’t contain a <code>String</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">someWork</span><span class="hljs-params">()</span> -&gt; <span class="hljs-title">Result</span>&lt;<span class="hljs-title">UUID</span>, <span class="hljs-title">any</span> <span class="hljs-title">Error</span>&gt;</span> {
  <span class="hljs-comment">// ...</span>
}
        </code></pre>
        <p>
          We need to pass a <code>Result&lt;String, any Error&gt;</code> to
          <code>MyData</code>. So we’ll need to transform this result.
        </p>
        <pre><code class="lang-swift">
func transform() -&gt; MyData {
  <span class="hljs-function"><span class="hljs-keyword">switch</span> <span class="hljs-title">someWork</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">case</span> .success(let uuid):
    MyData(result: .success(uuid.uuidString))
  <span class="hljs-keyword">case</span> .failure(let <span class="hljs-keyword">error</span>):
    MyData(result: .failure(<span class="hljs-keyword">error</span>))
  }
}
        </code></pre>
        <p>
          This is <em>painful</em> code to write. I don’t want to think about
          the failure case, instead I merely only care about the success case.
          So let’s turn to monads to cure our pain.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transform</span><span class="hljs-params">()</span> -&gt; <span class="hljs-title">MyData</span></span> {
  MyData(result: someWork().<span class="hljs-keyword">map</span>(\.uuidString))
}
        </code></pre>
        <p>
          Nice! However, what if instead of a uuid string, we treated the
          <code>UUID</code> as a user identifier of some kind, and then we
          wanted to look up the user name for that identifier. If the user
          doesn’t exist, we’ll return a failure result.
        </p>
        <pre><code class="lang-swift">
func username(<span class="hljs-keyword">for</span> id: UUID) -&gt; Result&lt;String, any Error&gt; {
  <span class="hljs-comment">// Return success if user with id exists, else failure...</span>
}

func transform() -&gt; MyData {
  <span class="hljs-comment">// ❌ Won't compile because map returns Result&lt;Result&lt;String, any Error&gt;, any Error&gt;</span>
  MyData(result: someWork().map { username(<span class="hljs-keyword">for</span>: $0) })
}

func compilingTransform() -&gt; MyData {
  <span class="hljs-comment">// Back to switch...</span>
  <span class="hljs-function"><span class="hljs-keyword">switch</span> <span class="hljs-title">someWork</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">case</span> .success(let uuid):
    <span class="hljs-keyword">switch</span> username(<span class="hljs-keyword">for</span>: uuid) {
    <span class="hljs-keyword">case</span> .success(let name):
      MyData(result: .success(name))
    <span class="hljs-keyword">case</span> .failure(let <span class="hljs-keyword">error</span>):
      MyData(result: .failure(<span class="hljs-keyword">error</span>))
    }
  <span class="hljs-keyword">case</span> .failure(let <span class="hljs-keyword">error</span>):
    MyData(result: .failure(<span class="hljs-keyword">error</span>))
  }
}
        </code></pre>
        <p>
          Again, this is painful because we have to think about the failure and
          success cases twice! My primary concern here is translating the UUID
          into a username, so let’s turn to monads to cure our pain.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transform</span><span class="hljs-params">()</span> -&gt; <span class="hljs-title">MyData</span> {</span>
  MyData(result: someWork().flatMap { username(<span class="hljs-keyword">for</span>: $0) })
}
        </code></pre>
        <p>
          In this case, “the magic stuff” (according to the ChatGPT explanation)
          is the handling the result for an operation. You don’t need to worry
          about the result handling, and instead you specify what the result
          handling means.
        </p>
        <br>
        <h2 id="pain-gives-meaning-to-monads">Pain Gives Meaning to Monads</h2>
        <p>
          You still don’t understand monads from those examples. You won’t until
          you feel the pain, the least I can do is show you what that pain feels
          like, such that you can anticipate it. Pain is a method of learning.
        </p>
        <p>
          Monads are actually an incredibly simple idea, just like this glass.
        </p>
        <img src="./monads/assets/glass.jpeg" alt="A glass that holds liquid.">
        <p>Yet how do you "learn a glass"? What does that even mean?</p>
        <p>
          A glass can do many things, it can hold liquid, dirt, toys, cash,
          coins, etc. It can be used to cover or trap something by acting as a
          surface. It’s also transparent, so we can see and learn from the
          internals of what it holds.
        </p>
        <p>
          Try drinking water without a glass or some other object. You’ll find
          this is painful, and you’ll use the glass as a cure. The pain gives
          meaning to the glass.
        </p>
        <p>
          Since the glass is so simple, we don’t derive meaning from it, we
          imbue it with meaning instead. Pouring different liquids give the
          glass a different meaning in a certain context. All of these meanings
          combined is what gives us an understanding of what a glass is.
        </p>
        <p>
          Understanding monads in programming has nothing to do with functors.
          endofunctors, or mathematical functions. Those terms do not describe
          the systems you’re building because they describe mathematics. You
          describe the systems you’re building with the artifacts at your
          disposal, and monads are an artifact to augment your ability to do so.
        </p>
        <p>So, to end this particular discussion.</p>
        <p>
          <strong><em>Don’t find meaning in Monads, give Monads your meaning.</em></strong>
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Personal Laws of Design</title>
      <link>https://whypeople.xyz/personal-laws-of-design</link>
      <guid isPermaLink="true">https://whypeople.xyz/personal-laws-of-design</guid>
      <pubDate>Sat, 12 Jul 2025 00:00:00 GMT</pubDate>
      <description>Laws I adhere to when designing technological systems.</description>
      <content:encoded><![CDATA[<h1 class="title" id="personal-laws-of-design">
          Personal Laws of Design
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <h2 id="collaboration-over-automation">
          Collaboration over Automation
        </h2>
        <p>
          Collaboration breeds an environment where systems and their users
          operate towards a discovered shared end goal, and enhances the innate
          abilities of all actors involved. Automation on the other hand, is
          driven solely by one actor towards their own imposed and possibly
          detrimental goal.
        </p>
        <p>
          Collaboration allows and encourages mistakes, and uses them to enhance
          innate ability of all actors. Automation on the other hand, punishes
          and forbids mistakes, and creates a cost center around them both
          monetarily and emotionally.
        </p>
        <p>
          Collaboration puts discovery, and searching for better strategies as
          its utmost priority. Automation on the other hand, discourages
          discovery in favor of optimizing what is already discovered.
        </p>
        <p>
          Collaboration encourages requests between actors, and encourages
          questioning the validity of both the request and its processing.
          Automation on the other hand, encourages one actor to command the
          others, and does not encourage questioning the validity of the command
          or its processing.
        </p>
        <p>
          Collaboration encourages a universal understanding of the larger
          picture. Automation on the other hand, promotes delegation and
          convenience at the expense of outsourcing such understanding.
        </p>
        <br>
        <h2 id="good-learning-and-teaching-are-experiential">
          Good Learning and Teaching are Experiential
        </h2>
        <p>
          Good learning is more than retaining or memorizing information. Good
          teaching is more than transferring information from point A to point
          B.
        </p>
        <p>
          Good learning involves the acceptance of said information into the
          functioning processes of the learner. Good teaching involves creating
          such an acceptance.
        </p>
        <p>
          Good learning involves more than seeing and consuming text, photos, or
          videos, but instead involves using all available senses (sight, taste,
          feel, sound, touch, etc.). Good teaching involves creating
          environments, media, and processes where such senses can be utilized
          to their utmost effectiveness.
        </p>
        <p>
          Good learning involves blending information with actionable scenarios,
          and correlating the two. Good teaching involves the creation of such
          scenarios blended with the relevant information.
        </p>
        <p>
          Good learning means understanding the relations of isolated
          information into larger contexts. Good teaching involves communicating
          the larger context when isolated information is presented.
        </p>
        <p>
          Good learning emphasizes experiencing information. Good teaching
          emphasizes creating such experiences.
        </p>
        <br>
        <h2 id="evolution-is-a-design-decision">
          Evolution is a Design Decision
        </h2>
        <p>
          Evolution is essential for thriving. Needs, wants, and circumstances
          can change at a moment’s notice. Systems that cannot evolve when
          necessary will eventually collapse.
        </p>
        <p>
          Evolution requires understanding the conceptual models that the system
          offers, and the contexts in which they work and don’t work in. Such an
          understanding gives prose to where and how evolution must occur.
        </p>
        <p>
          Evolution requires dependence on abstractions and generalities, and
          not particulars that require evolution at different rates. That is to
          say, loose coupling between evolvable components is necessary for
          creating evolvable boundaries in the larger system.
        </p>
        <p>
          Evolution requires understanding and exploring different
          possibilities, and is not focused on concrete steps that must be
          adhered to.
        </p>
        <br>
        <h2 id="context-is-king">Context is King</h2>
        <p>
          Context denotes the decisions that are right and wrong. Some decisions
          can be right in certain contexts, and wrong in others.
        </p>
        <p>
          Context requires less dogmatism, and more pragmatism. Even my personal
          design laws must be applied pragmatically, and trade offs and
          variances must be considered in different contexts.
        </p>
        <p>
          Context requires understanding systems and ideas in their entirety,
          and which parts of the systems or ideas apply to a specific context.
          Individual parts of evolvable systems in different contexts should be
          applicable to other contexts, even if not in whole.
        </p>
        <p>
          Context requires ideas to fit within it. General ideas should be
          applicable to a large amount of contexts, but not need to service the
          entirety of such contexts. Particular ideas should be applicable to a
          smaller number of contexts, but should service them more in their
          entirety. General ideas defines how the particular ideas are formed
          within a context.
        </p>
        <p>
          Context requires understanding possibilities, and how those
          possibilities are affected by the context. In such a case, the context
          serves as a factor in the equation for the result of a possibility.
          The ability to quickly experiment with different possibilities in a
          context is essential for making correct decisions.
        </p>
        <br>
        <h2 id="objects-are-best-used-as-agents-of-knowledge">
          Objects are Best Used as Agents of Knowledge
        </h2>
        <p>
          Objects encapsulate knowledge they understand. Such knowledge can
          represent the static bits of information in their corpus, but also the
          processes for representing such information which are static bits of
          information themselves.
        </p>
        <p>
          Objects take many forms of representation (code, neurons, physical
          tools, etc.), and provide interfaces for interacting with their
          knowledge. Whatever is behind those interfaces is what is encapsulated
          within the object. Yet, the interface itself is knowledge that an
          object encapsulates, and represents to the outer world.
        </p>
        <p>
          Objects that can learn from their contexts are rich in individual
          meaning, encapsulate lots of knowledge, and have the ability to evolve
          into different contexts using their pool of knowledge. Such objects
          should enrich their context by providing highly capable interfaces to
          request information from them in order to aid evolution. If such an
          object were to provide an interface that commands its actions rather
          than request its information, then it is not being used to enrich its
          context, and it would not be compatible with evolution.
        </p>
        <p>
          Objects that are static in their knowledge require their surrounding
          context to give meaning to them, encapsulate little, and have little
          ability to evolve on their own. Such objects should invite their usage
          by providing a small and simple interface to interact with them to aid
          evolution. If such an object were to expose a large and informational
          interface, then it cannot not be given full meaning in different
          contexts, and it would not be compatible with evolution.
        </p>
        <br>
        <h2 id="human-wisdom-must-contain-human-power">
          Human Wisdom must Contain Human Power
        </h2>
        <p>
          Human wisdom increases when systems are designed to be collaborative
          instead of automative.
        </p>
        <p>
          Human wisdom increases when systems can teach their users through
          experience, and encourage their users to learn from their teachings.
        </p>
        <p>
          Human wisdom increases when systems can evolve to serve their user’s
          ever changing needs at the moment those needs become apparent.
        </p>
        <p>
          Human wisdom increases when systems are designed to correctly fit
          their context, and make use of the appropriate principles at the
          correct volume to do so.
        </p>
        <p>
          Human wisdom increases when systems present objects with the
          appropriate interfaces given their levels of knowledge.
        </p>
        <p>
          <strong>Human wisdom must contain human power, or else we risk terrible
            consequences.</strong>
        </p>
        <p>— 7/12/2025</p>
        <br>
        <hr>
        <br>
        <h2>Ending Commentary</h2>
        <p>
          Some laws I adhere to in design. Design to me does not solely mean the
          act of making a product look incredible, easy to use, or accessible as
          that is quite a narrow view point on real systems. It also does not
          solely refer design patterns in code or overall software architecture.
          Both these perspectives are essential, but are too narrow to consider
          in isolation when considering entire systems.
          <a href="https://www.google.com/search?q=the%20design%20of%20everyday%20things&amp;ie=utf-8&amp;oe=utf-8&amp;client=firefox-b-1-m">The Design of Everyday Things</a>
          by Don Norman gives great insight, as well as the ideas of many others
          (especially Bret Victor).
        </p>
        <p>
          These laws are meant to be applied to technological systems in
          different domains (programming, UI Design, project management, etc.).
          I also belive that these laws have been largely ignored in todays
          systems, and that is naturally something I would like to change.
        </p>
        <p>
          <strong>Update:</strong> Some have asked about specific examples of
          where I think these laws have been violated. Much of my other writing
          contains examples of these violations, and I intend to explore more
          examples in future writing. However, doing so here would make readers
          focus on the examples rather than the true meaning of the laws
          themselves. In other words, I don't want this to attract comments such
          as, "ThIS eXamPLE dOESn'T tALk aBoUt tHiS nEW fEatURE iN jAVa, sO tHE
          enTIRe aRTiCLE (which isn't about Java) iS bS."
        </p>]]></content:encoded>
    </item>
    <item>
      <title>A Realization on Low and High Level Languages</title>
      <link>https://whypeople.xyz/a-realization-on-low-and-high-level-languages</link>
      <guid isPermaLink="true">https://whypeople.xyz/a-realization-on-low-and-high-level-languages</guid>
      <pubDate>Sun, 29 Jun 2025 00:00:00 GMT</pubDate>
      <description>Low and high level languages are tied to systems, not computer architecture.</description>
      <content:encoded><![CDATA[<h1 id="a-realization-on-low-and-high-level-languages">
          A Realization on Low and High Level Languages
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          If you write code, you’re probably familiar with the concept of high
          and low level languages. In technical terms, low level means closer to
          the machine, like assembly languages or machine code. Higher level in
          technical terms means further from the machine like C, JavaScript,
          Swift, and so on.
        </p>
        <p>
          However, this shouldn’t be the case. Speaking of high and low levels
          in the context of how close a language is to computer architecture is
          quite a narrow view point on the world.
        </p>
        <p>
          Rather, in the context of “real systems”, this distinction should be
          reversed. In other words, C, JavaScript, and Swift are lower-level
          languages. Meanwhile, assembly and machine code are higher-level
          languages.
        </p>
        <p>
          For instance, if we’re writing a pseudo random number generator, which
          language do you think gives you a better <em>understanding</em> about
          <em>the process</em> of how random numbers are generated?
        </p>
        <p><strong>x86-64 Assembly</strong></p>
        <pre><code class="lang-x86">
<span class="hljs-symbol">rotl:</span>
        <span class="hljs-keyword">push</span>    <span class="hljs-built_in">rbp</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rbp</span>, <span class="hljs-built_in">rsp</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">8</span>], <span class="hljs-built_in">rdi</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">DWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">12</span>], <span class="hljs-built_in">esi</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">eax</span>, <span class="hljs-built_in">DWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">12</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">8</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">ecx</span>, <span class="hljs-built_in">eax</span>
        <span class="hljs-keyword">rol</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">cl</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">pop</span>     <span class="hljs-built_in">rbp</span>
        <span class="hljs-keyword">ret</span>

<span class="hljs-symbol">xoshiro_256_next:</span>
        <span class="hljs-keyword">push</span>    <span class="hljs-built_in">rbp</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rbp</span>, <span class="hljs-built_in">rsp</span>
        <span class="hljs-keyword">push</span>    <span class="hljs-built_in">rbx</span>
        <span class="hljs-keyword">sub</span>     <span class="hljs-built_in">rsp</span>, <span class="hljs-number">24</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>], <span class="hljs-built_in">rdi</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">8</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">sal</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">2</span>
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">esi</span>, <span class="hljs-number">7</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdi</span>, <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">call</span>    rotl
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">sal</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">3</span>
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">16</span>], <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">8</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">sal</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">17</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">24</span>], <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">16</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rcx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">16</span>
        <span class="hljs-keyword">xor</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">rcx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>], <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">24</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rcx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">8</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">24</span>
        <span class="hljs-keyword">xor</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">rcx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>], <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">8</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rcx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">16</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">8</span>
        <span class="hljs-keyword">xor</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">rcx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>], <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">24</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">xor</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>], <span class="hljs-built_in">rdx</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">16</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-number">16</span>
        <span class="hljs-keyword">xor</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">24</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rdx</span>], <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">add</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-number">24</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rax</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">32</span>]
        <span class="hljs-keyword">lea</span>     <span class="hljs-built_in">rbx</span>, [<span class="hljs-built_in">rdx</span>+<span class="hljs-number">24</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">esi</span>, <span class="hljs-number">45</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rdi</span>, <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">call</span>    rotl
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbx</span>], <span class="hljs-built_in">rax</span>
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">16</span>]
        <span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rbx</span>, <span class="hljs-built_in">QWORD</span> <span class="hljs-built_in">PTR</span> [<span class="hljs-built_in">rbp</span>-<span class="hljs-number">8</span>]
        <span class="hljs-keyword">leave</span>
        <span class="hljs-keyword">ret</span>
        </code></pre>
        <p><strong>C</strong></p>
        <pre><code class="lang-c">
#include &lt;stdint.h&gt;

static inline uint64_t rotl(const uint64_t x, int k) {
  return (x &lt;&lt; k) | (x &gt;&gt; (<span class="hljs-number">64</span> - k));
}

uint64_t xoshiro_256_next(uint64_t s[<span class="hljs-number">4</span>]) {
  const uint64_t result = rotl(s[<span class="hljs-number">1</span>] * <span class="hljs-number">5</span>, <span class="hljs-number">7</span>) * <span class="hljs-number">9</span>;
  const uint64_t t = s[<span class="hljs-number">1</span>] &lt;&lt; <span class="hljs-number">17</span>;

  s[<span class="hljs-number">2</span>] ^= s[<span class="hljs-number">0</span>];
  s[<span class="hljs-number">3</span>] ^= s[<span class="hljs-number">1</span>];
  s[<span class="hljs-number">1</span>] ^= s[<span class="hljs-number">2</span>];
  s[<span class="hljs-number">0</span>] ^= s[<span class="hljs-number">3</span>];
  s[<span class="hljs-number">2</span>] ^= t;
  s[<span class="hljs-number">3</span>] = rotl(s[<span class="hljs-number">3</span>], <span class="hljs-number">45</span>);
  return result;
}
        </code></pre>
        <p>
          I would imagine that you find that the C code describes the process of
          generating a random number more clearly. In other words, the C code
          describes reality more than x86 assembly.
        </p>
        <p>Take a line from the assembly example.</p>
        <pre><code class="lang-x86"><span class="hljs-keyword">mov</span>     <span class="hljs-built_in">rax</span>, <span class="hljs-built_in">rdx</span></code></pre>
        <p>
          <code>mov</code> is a move instruction, and <code>rax</code> and
          <code>rdx</code> are registers. As a whole, a move instruction can be
          applied in an infinite number of programs, and <code>rax</code> and
          <code>rdx</code> will also be a part of those infinite numbers of
          programs. In other words, a move instruction is
          <em>very high level</em> because it doesn’t describe the domain
          specific processes of any particular program. It is therefore detached
          from reality.
        </p>
        <p>
          Notice how the concept of registers and instructions are only directly
          relevant in the context of the assembly example. We don’t need to talk
          about these things in C, because they are ultimately
          <em>irrelevant</em> (only in the sense of understanding, not
          execution) to the <em>larger process</em> of generating a random
          number. The C code is closer to reality because it describes such
          reality more accurately from an <em>understandings</em> stand point.
        </p>
        <p>
          In assembly, because we’re so focused on primitive details like
          registers and instructions, we easily miss the larger picture. We
          therefore miss <em>reality</em>.
        </p>
        <p>
          To conclude here, if we want to build great systems, we need to
          optimize for understanding and reasoning about our systems. Computer
          architecture is an implementation detail that is largely detached from
          reality as it says nothing about our systems on its own. Sticking
          closer to computer architecture may produce faster and more performant
          programs, but it is often detached from the deeper truth of what we
          directly create.
        </p>
        <p>
          All in all, lower level means providing both full and real awareness
          of the true reality of our systems, and higher level detaches itself
          from that in order to focus more on abstractions. In this sense,
          Assembly is higher level than C.
        </p>
        <p>
          To end, in this exploration I stayed close to existing languages that
          are used in the mainstream. I suppose one could therefore ask about a
          language so low level that it describes and enhances reality
          perfectly, and how that would look and operate. That is, something
          that indeed may <a href="https://dynamicland.org/">exist</a> in a
          primitive form.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Swift Query, a Library for Asynchronous State Management</title>
      <link>https://whypeople.xyz/swift-query</link>
      <guid isPermaLink="true">https://whypeople.xyz/swift-query</guid>
      <pubDate>Sun, 29 Jun 2025 00:00:00 GMT</pubDate>
      <description>Bringing over Tanstack Query into Swift.</description>
      <content:encoded><![CDATA[<h1 class="title" id="swift-query-a-library-for-asynchronous-state-management">
          Swift Query, a Library for Asynchronous State Management
        </h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          These past few months, I’ve been working on standardizing a set of
          tools that I’ve been using across a few of my Swift projects. Today,
          I’m opening them up to you to use in your applications, and I believe
          these tools will largely remove headaches around data consistency from
          remote/asynchronous data for you. This article will largely be about
          my inspirations for building such a library, and what I think it means
          for how we manage application state derived from asynchronous data in
          Swift.
        </p>
        <p>
          You can check out the library
          <a href="https://github.com/mhayes853/swift-query">here</a> for a more
          direct dive into its features, and the many demos, case studies, and
          documentation articles I’ve prepared. I believe that this library is
          quite flexible, and can be used in a variety of ways that I don’t even
          know of yet. I wanted to document as many of the strategies that I’ve
          used it in real projects in its documentation, and I believe you will
          also be able to find ones that I haven’t figured out myself. If you
          find some cool use cases that I haven’t documented, let me know such
          that we can either get a case study or an article going.
        </p>
        <p>
          As a whole, I want to keep this article related to the higher-level
          problems that are being solved by the library. Checkout the
          <a href="https://github.com/mhayes853/swift-query">repo</a> if you
          want a typical overview of its features with plenty of examples.
        </p>
        <br>
        <h2 id="background-and-inspirations">Background and Inspirations</h2>
        <p>
          The past 1.5 years using React Native at a startup is largely
          responsible for my inspirations for building Swift Query. In
          particular, I was often responsible for combining all sorts of
          data (Location, Travel Estimates, Geo-spatially Separated Events,
          Arrivals at Locations, and of course “Users”) from different sources
          (Device APIs, SQLite, AWS, and our REST API). Additionally, much of
          this data also needed to be observed in real-time. In other words,
          it’s quite safe to say that many of us have worked on applications
          like these, and managing such data in many different ways is common
          place for today’s apps.
        </p>
        <p>
          That being said, all of this data comes asynchronously, and takes a
          non-trivial but varying amounts of time to load from its source. The
          code itself to fetch the data often isn’t the hard part (unless it’s
          paired with complex algorithmic processing afterwards), and can be
          done in just a few lines for most data sources.
        </p>
        <p>
          Yet, what does it mean to load asynchronous data? Such data comes
          from the physical world, because something physical must work to
          provide it, otherwise it we wouldn’t consider it “asynchronous”. As
          software developers, we rarely ever have to think of design in the
          context of the physical world. Rather, we love to create our own
          abstract worlds, and demand physical reality bends to our world. As
          such, we loathe error handling, keeping asynchronous data consistent
          when displayed in the UI, and creating systems with the expectation
          of failure. This is especially difficult considering modern UI
          frameworks are declarative and are driven via local state variables,
          whereas most data fetching is strictly imperative and affects the
          application’s global state.
        </p>
        <p>
          <strong><em>Yet, if we want to build great experiences for our users, we need
              to collaborate with physical reality, and augment their ability
              beyond physical reality in our abstract worlds.</em></strong>
        </p>
        <p>
          This all sounds incredibly philosophical, but it is exactly the reason
          why <a href="https://tanstack.com/query/latest">tanstack query</a> is
          popular in the JavaScript world. The meaning of asynchronous data is
          incredibly complex in any given system, and if we’re to collaborate
          with physical reality we must <em>design</em> for failure. As such, we
          must implement retry mechanisms, caching, informative loading/error UI
          states, cancellation, keeping data consistent between screens,
          automatic refetching, stale-while-revalidate, network connection
          monitoring, exponential/fibonacci/linear backoff strategies,
          pagination, deduplication, and logging on top of our already existing
          application features. Tanstack query has an answer for all of this in
          the JavaScript world.
        </p>
        <p>
          I get it, you have better things to be doing rather than implementing
          all of those mechanisms to collaborate with physical reality in Swift.
          Your users also think the same for the record. Therefore, I have
          decided to build a port of Tanstack Query that’s tuned for Swift,
          SwiftUI,
          <a href="https://github.com/pointfreeco/swift-sharing">swift-sharing</a>, Concurrency, UIKit, Linux, WASM, and much more in the future. I
          want this library to aid the development of Swift as a true
          cross-platform language by solving such a common problem at the most
          literal level possible.
        </p>
        <p>
          I built this library because I missed Tanstack Query when working in
          Swift.
        </p>
        <br>
        <h2 id="addressing-tanstack-query-s-limitations-in-swift-query">
          Addressing Tanstack Query’s Limitations in Swift Query
        </h2>
        <p>
          Additionally, I aim to address many long-standing issues that Tanstack
          Query has in order to make something that can fit in to any of your
          asynchronous data needs. In other words, this library is meant to
          provide you a framework of thinking about asynchronous data by
          providing far more generic abstractions than Tanstack Query. This in
          turn may increase the overall learning curve of the library, but such
          a fate is better than it not being able to serve your needs.
        </p>
        <h3 id="automatic-refetching">Automatic Refetching</h3>
        <p>
          Tanstack Query will automatically refetch data when the user
          foregrounds your app, or the moment user’s network connection status
          flips from offline to online. Swift Query also provides these tools,
          but it also allows you to create your own custom refetch triggers by
          using the <code>FetchCondition</code> protocol. Therefore, you could
          make it so that data is automatically refetched when a user logs
          in/out, or when
          <a href="https://github.com/mhayes853/swift-query/blob/main/Examples/CaseStudies/CaseStudies/03-CustomFetchConditions.swift">low-power mode is enabled</a>, and much more.
        </p>
        <h3 id="data-fetching-methods">Data Fetching Methods</h3>
        <p>
          Tanstack Query has 3 methods of fetching data suited for different
          purposes:
        </p>
        <ul>
          <li>
            Queries
            <ul>
              <li>
                Fetching data as a single entire entity (this is the most common
                method).
              </li>
            </ul>
          </li>
          <li>
            Mutations
            <ul>
              <li>Mutating asynchronous data. (eg. HTTP POST request)</li>
            </ul>
          </li>
          <li>
            Infinite Queries
            <ul>
              <li>For paginating data (eg. Infinite scrolling lists).</li>
            </ul>
          </li>
        </ul>
        <p>
          Swift Query provides these 3 methods out of the box, but also allows
          you to create your own methods via the
          <code>QueryRequest</code> protocol. For instance, you could create a
          method that specializes in fetching recursive data, or combining
          multiple paginated data streams together.
        </p>
        <p>
          Another difference from Tanstack Query is that in Swift Query,
          infinite queries and mutations are derived from typical queries. That
          is, <code>MutationRequest</code> and
          <code>InfiniteQueryRequest</code> are conformances to
          <code>QueryRequest</code>. In Tanstack Query, mutations are a
          completely separate concept not derived from queries, and there’s even
          a special <code>MutationCache</code> for caching their state. Such a
          cache is not needed in Swift Query, because mutations are queries.
          Additionally, deriving both infinite queries and mutations from
          typical queries allows <code>QueryModifier</code> conformances to work
          with all 3 methods by default.
        </p>
        <p>
          Swift Query also differs slightly in how infinite query refetching
          works. In Tanstack Query, refetching an infinite query would refetch
          every single page starting from the beginning in a waterfall style. In
          Swift Query, only the initial page is refetched, and all other pages
          are simply removed. If you want to retain the Tanstack Query behavior,
          calling <code>refetchAllPages</code> on a <code>QueryStore</code> will
          do exactly that.
        </p>
        <p>
          As for mutations, Swift Query adds 1 additional feature not found in
          Tanstack Query. In Tanstack Query, multiple calls to
          <code>mutation.mutate</code> will run concurrently, however the
          mutation state from <code>useMutation</code> only displays updates
          from <em>the most recent</em> call. Swift Query also works this way,
          but an additional <code>history</code> property exists that allows you
          to inspect the previous results from other calls to
          <code>mutate</code>.
        </p>
        <h3 id="query-cache">Query Cache</h3>
        <p>
          Tanstack Query holds all of your query data in something called the
          <code>QueryCache</code>. This cache will evict entries based on a
          running timer in order to properly manage memory. Swift Query also
          implements uses a similar cache, but it evicts entries based on memory
          pressure warnings from the operating system instead of by a timer.
          However, the good news is that you can write your own custom query
          cache in Swift Query by conforming to the
          <code>QueryClient.StoreCache</code> protocol. Therefore, if you want
          the timer approach, you have the option to implement it yourself.
        </p>
        <h3 id="query-client">Query Client</h3>
        <p>
          The <code>QueryClient</code> in Tanstack Query holds all of your
          queries, and allows you to operate on them (eg. Refetching,
          invalidation, etc.). This is also the case for Swift Query, except
          that you aren’t required to attach a query to the
          <code>QueryClient</code>. You can use
          <code>QueryStore.detached</code> instead of obtaining a
          <code>QueryStore</code> through a <code>QueryClient</code>, and this
          will give you a store that runs its query in isolation. By doing this,
          you can even create your own state management system if you find that
          <code>QueryClient</code> does not suit your needs.
        </p>
        <p>
          Additionally, the way one interacts with <code>QueryClient</code> in
          Tanstack Query is by calling methods such as <code>refetch</code> or
          <code>invalidate</code> directly on the client. Swift Query does not
          provide those methods on <code>QueryClient</code>. Instead, it
          provides methods that return <code>QueryStore</code> instances
          directly, and <code>QueryStore</code> has the appropriate refetch and
          invalidation methods. This allows you to perform advanced and
          composable operations on the <code>QueryClient</code> without you
          having to pester me to add a new method on <code>QueryClient</code>.
        </p>
        <p>
          For example, the following (which is real-world code) can be done in 1
          operation. Here, we’re removing queries from the client that
          correspond to files being deleted from a directory. Then, we’re
          refetching all queries that have a corresponding file that has had its
          data updated, and that are actively being displayed in the visible UI
          somewhere.
        </p>
        <pre><code class="lang-swift">
extension QueryClient {
  public func processDirectoryChanges(
    _ changes: IdentifiedArrayOf<hapticaaudiofilesdirectory.subscriptionpayload.change>
  ) async throws {
    let storesToRefetch = self.withStores(
      matching: .audioProperties,
      of: HapticaAudioProperties.Query.State.self
    ) { entries in
      let filesToRemove = changes.filter { $0.kind == .deleted }
        .map { HapticaAudioFile(id: $0.id) }
      for file in filesToRemove {
        entries.removeValue(forPath: .audioProperties(for: file))
      }
      return changes.filter { $0.kind == .dataUpdated }
        .compactMap { entries[.audioProperties(for: HapticaAudioFile(id: $0.id))] }
        .filter { $0.subscriberCount &gt; 0 }
    }
    await withThrowingTaskGroup(of: Void.self) { group in
      for store in storesToRefetch {
        group.addTask { try await store.fetch() }
      }
    }
  }
}
        </hapticaaudiofilesdirectory.subscriptionpayload.change></code></pre>
        <p>
          You have access to all the necessary properties and methods directly
          on <code>QueryStore</code> to perform whatever state management you
          need. The <code>withStores</code> method takes a closure that allows
          you to edit the existing stores inside the
          <code>QueryClient</code> like you would a dictionary.
        </p>
        <h3 id="query-keys">Query Keys</h3>
        <p>
          Swift Query’s <code>QueryClient</code> also supports pattern matching
          using a query key like in Tanstack Query. However, the query key is
          called a <code>QueryPath</code> in Swift Query, and
          <code>QueryPath</code> is a dedicated struct that can be constructed
          with an array literal of Hashable and Sendable elements.
        </p>
        <p>
          Unlike Tanstack Query, your query <em>is not</em> required to specify
          a <code>QueryPath</code> if it conforms to Hashable or Identifiable.
          In such a case, the <code>QueryRequest</code> synthesizes a
          conformance for you. To visualize:
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">MyQuery</span>: QueryRequest, Hashable </span>{
  <span class="hljs-comment">// Does not need a query path...</span>
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">MyQuery2</span>: QueryRequest </span>{
  <span class="hljs-comment">// You must provide a query path because it is not Hashable or Identifiable...</span>
  var path: QueryPath {
    [<span class="hljs-string">"my-query-2"</span>]
  }
}
        </code></pre>
        <p>
          As an internal implementation note, a query key in Tanstack Query is
          represented by the full bloat of a JavaScript array. In Swift Query,
          the <code>QueryPath</code> type is optimized to not allocate an array
          when the path is either empty or only contains a single element.
        </p>
        <p>
          By using a dedicated type for the paths, you can also create dedicated
          domain-specific constants by extending <code>QueryPath</code>.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">QueryPath</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> currentUser = <span class="hljs-type">Self</span>(<span class="hljs-string">"current-user"</span>)

  <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">currentUserProject</span><span class="hljs-params">(<span class="hljs-keyword">for</span> projectId: Int)</span></span> -&gt; <span class="hljs-type">Self</span> {
    .currentUser.appending(projectId)
  }
}
        </code></pre>
        <h3 id="query-options">Query Options</h3>
        <p>
          In Tanstack Query, you customize the behavior of a query by passing a
          bag of options, which looks like such:
        </p>
        <pre><code class="lang-ts">
const myQuery = useQuery({
<span class="hljs-symbol">  queryKey:</span> [<span class="hljs-string">"my-query"</span>],
<span class="hljs-symbol">  queryFn:</span> async () =&gt; {
    <span class="hljs-comment">// Fetch the data...</span>
  },
<span class="hljs-symbol">  retry:</span> <span class="hljs-number">3</span>,
<span class="hljs-symbol">  staleTime:</span> <span class="hljs-number">5000</span>
})
        </code></pre>
        <p>
          The problem with this approach is that the structure of the object is
          solely determined by the maintainers of Tanstack Query. You are
          essentially stuck with whatever configuration is deemed necessary by
          the library. There are ways around this, but generally they are not
          the most intuitive.
        </p>
        <p>
          Instead, Swift Query defines queries as types, which allows for a
          modifier system similar to SwiftUI. In fact, you can create your own
          custom modifiers through the <code>QueryModifier</code> protocol. The
          disadvantage of this however is that queries require a some additional
          boilerplate due to needing to create an entire separate type for each
          one.
        </p>
        <p>Here’s the same query written in swift query.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">MyQuery</span>: <span class="hljs-title">QueryRequest</span> </span>{
  <span class="hljs-keyword">var</span> path: <span class="hljs-type">QueryPath</span> { <span class="hljs-type">QueryPath</span>(<span class="hljs-string">"my-query"</span>) }

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    <span class="hljs-keyword">in</span> context: QueryContext,
    with continuation: QueryContinuation&lt;Value&gt;
  )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Value</span> {
    <span class="hljs-comment">// Fetch the data...</span>
  }
}

<span class="hljs-keyword">let</span> myQuery = <span class="hljs-type">MyQuery</span>().retry(limit: <span class="hljs-number">3</span>).stale(after: <span class="hljs-number">5</span>)
        </code></pre>
        <p>
          Having a custom modifier system means that you can configure your
          queries in ways that the library doesn’t natively support, and work
          such support into your own app. For instance, the library doesn’t ship
          with a native method to artificial delay a query, but such a modifier
          can easily be written and used across many different queries.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DelayModifier</span>&lt;<span class="hljs-title">Query</span>: <span class="hljs-title">QueryRequest</span>&gt;: <span class="hljs-title">QueryModifier</span> </span>{
  <span class="hljs-keyword">let</span> delay: <span class="hljs-type">Duration</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    <span class="hljs-keyword">in</span> context: QueryContext,
    using query: Query,
    with continuation: QueryContinuation&lt;Query.Value&gt;
  )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Query</span>.<span class="hljs-type">Value</span> {
    <span class="hljs-keyword">try</span> await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: <span class="hljs-keyword">self</span>.delay)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await query.fetch(<span class="hljs-keyword">in</span>: context, with: continuation)
  }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">QueryRequest</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">delay</span><span class="hljs-params">(<span class="hljs-keyword">for</span> duration: Duration)</span></span> -&gt; <span class="hljs-type">ModifiedQuery</span>&lt;<span class="hljs-type">Self</span>, <span class="hljs-type">DelayModifier</span>&lt;<span class="hljs-type">Self</span>&gt;&gt; {
    <span class="hljs-keyword">self</span>.modifier(<span class="hljs-type">DelayModifier</span>(delay: duration))
  }
}

<span class="hljs-keyword">let</span> myQuery = <span class="hljs-type">MyQuery</span>().delay(<span class="hljs-keyword">for</span>: .seconds(<span class="hljs-number">0.3</span>))
        </code></pre>
        <h3 id="multiple-data-updates">Multiple Data Updates</h3>
        <p>
          Tanstack Query provides a <code>streamedQuery</code> utility to allow
          you to yield multiple updates from your queries. This function
          requires that your query returns a JavaScript
          <code>AsyncIterable</code>, and the library handles the rest. Instead,
          Swift Query allows you to yield multiple updates from your queries via
          the <code>QueryContinuation</code> handed to your query.
        </p>
        <p>This makes it easy to implement patterns such as.</p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeQuery</span>: <span class="hljs-title">QueryRequest</span>, <span class="hljs-title">Hashable</span> </span>{
  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    <span class="hljs-keyword">in</span> context: QueryContext,
    with continuation: QueryContinuation&lt;Value&gt;
  )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Value</span> {
    <span class="hljs-keyword">let</span> localValue = <span class="hljs-keyword">try</span> await <span class="hljs-type">LocalDatabase</span>.fetchValue()
    continuation.yield(localValue)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await <span class="hljs-type">Server</span>.fetchValue()
  }
}
        </code></pre>
        <p>
          Ultimately, you’re still forced to return a final value from your
          query. If that value is no different from the last yielded value, then
          simply return the last yielded value.
        </p>
        <br>
        <h2 id="integrating-with-your-app-architecture">
          Integrating with your App Architecture
        </h2>
        <p>
          One of my core principles with this library was to not care about your
          architecture as much as possible. That is, whether or not you use
          MVVM, TCA, MV, or some other combination of MXX, the library should
          still work for your use case. From my usage so far, I’ve had no
          trouble using the library in each of those architectural patterns. I
          also have taken making the act of writing tests heavily into
          consideration, and all the demos and case studies in the repo have
          sufficient test coverage to demonstrate this.
        </p>
        <h3 id="concurrency">Concurrency</h3>
        <p>
          The library is also on the bleeding edge of Swift Concurrency, and
          provides tools that let you run your queries on custom
          <code>TaskExecutor</code> conformances and more. Everything is
          appropriately <code>Sendable</code>, and you can use the library from
          any actor or thread in your app. The only limitations are that the
          data types you return from your queries must also be
          <code>Sendable</code>, your query structs themselves must also be
          <code>Sendable</code>, and to make sure that you hop to the main
          thread if you’re observing <code>QueryStore</code> directly for UI
          updates.
        </p>
        <p>
          The library also runs its work via the <code>QueryTask</code> type.
          This is a special wrapper around a typical Swift
          <code>Task</code> that doesn’t begin scheduling its work immediately
          after initialization. Rather, you tell it explicitly when to run via
          <code>runIfNeeded</code>, and you can also use it to schedule work in
          a serial manner compared to other tasks via calling
          <code>task1.schedule(after: task2)</code>. If you end up scheduling
          tasks in a cycle, the library will warn you about this. In particular,
          you’ll find the flexibility of this type to be very useful for
          testing, as it gives you control over when your query is executed.
        </p>
        <h3 id="network-layers">Network Layers</h3>
        <p>
          Another principle of the library is abtstracting over an
          <code>async</code> function in general, and not over the network.
        </p>
        <p>
          You may find that you want to write all of your networking code
          directly inline inside your query structs, like so.
        </p>
        <pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">UserQuery</span>: <span class="hljs-title">QueryRequest</span>, <span class="hljs-title">Hashable</span> </span>{
  <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>

  <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetch</span><span class="hljs-params">(
    <span class="hljs-keyword">in</span> context: QueryContext,
    with continuation: QueryContinuation&lt;User&gt;
  )</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">User</span> {
    <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.myapp.com/user/<span class="hljs-subst">\(<span class="hljs-keyword">self</span>.id)</span>"</span>)!
    <span class="hljs-keyword">let</span> (data, <span class="hljs-number">_</span>) = <span class="hljs-keyword">try</span> await <span class="hljs-type">URLSession</span>.shared.data(from: url)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">User</span>.<span class="hljs-keyword">self</span>, from: data)
  }
}
        </code></pre>
        <p>
          Certainly, you can do this, and it is definitely fine for smaller
          apps.
          <strong><em>However, Swift Query was designed to enhance your network layer,
              and not replace it.</em></strong>
          In fact, you’ll find that in all of the demos and case studies that
          the query structs themselves are actually quite small, and that they
          usually call out to a method (on some API class) that actually
          performs the real data fetching.
        </p>
        <p>
          Regardless, the library’s choice to abstract over a plain
          <code>async</code> function allows you to implement your queries no
          matter how well defined your app’s network layer is.
        </p>
        <br>
        <h2 id="future-directions">Future Directions</h2>
        <p>
          In general, I think the library provides all of the essentials today,
          but there are a few things that I want to look into in the potential
          future.
        </p>
        <ol>
          <li>
            <strong>Better defaults on Linux.</strong>
            <ul>
              <li>
                As of now, the library will compile and run on Linux, but there
                is no default <code>NetworkObserver</code> or
                <code>ApplicationActivityObserver</code> provided by the
                library. As such, you’ll be forced to create your own
                conformances if you want your queries to automatically be
                refetched when the your app foregrounds, or when the user’s
                network connection flips from offline to online.
              </li>
            </ul>
          </li>
          <li>
            <strong>A better default store cache on non-Apple platforms.</strong>
            <ul>
              <li>
                Currently, the default store cache listens for memory pressure
                warnings from the operating system via
                <code>DispatchSource.makeMemoryPressureSource</code>. This API,
                or really it seems any memory pressure API, is not available on
                non-Apple platforms. As of now, the default store cache on
                non-Apple platforms will not automatically cleanup unused
                queries in any way. Of course, you can implement your own store
                cache to get around this, but a better default would be ideal
                here.
              </li>
            </ul>
          </li>
          <li>
            <strong>Typed <code>QueryContext</code>.</strong>
            <ul>
              <li>
                Right now the <code>QueryContext</code> is untyped and has no
                generics. As such, context properties often have to be stored as
                <code>any Sendable</code> or <code>any MyProtocol</code>, and
                then casted to the appropriate strong types in the right
                contexts. I would like to see if it’s possible to add some sort
                of strong typing to this in the future.
              </li>
            </ul>
          </li>
          <li>
            <strong>An official headless mode.</strong>
            <ul>
              <li>
                In some cases, you may want to run a query in some background
                process (eg. In a <code>Tool</code> in the new Foundation Models
                framework) instead of in the UI. This way, you can still benefit
                from modifiers such as retries, exponential backoff, duration
                logging, and much more. However, doing so would require
                constructing a <code>QueryStore</code>, which is explicitly
                meant for managing the query’s active running state in your
                application. In a background context, this additional state
                management is largely unnecessary because you’re not rendering
                any UI.
              </li>
            </ul>
          </li>
        </ol>
        <br>
        <h2 id="start-using-swift-query-today">
          Start Using Swift Query Today
        </h2>
        <p>
          You can start using Swift Query today in your apps.
          <strong>However, I have not yet declared that the library is fully stable,
            so proceed at your own risks.</strong>
          In any case, try it out and let me know how it goes.
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Thoughts on Liquid Glass</title>
      <link>https://whypeople.xyz/liquid-glass</link>
      <guid isPermaLink="true">https://whypeople.xyz/liquid-glass</guid>
      <pubDate>Sat, 14 Jun 2025 00:00:00 GMT</pubDate>
      <description>What I think of Apple&apos;s new aesthetic in the grand scheme of things.</description>
      <content:encoded><![CDATA[<p class="article-reading-time">-- Minutes to Read</p>

        <p>
          I’ve installed the new iPadOS 26 beta on my iPad Pro this week, and
          have built an Xcode project a singular time on an iOS 26 simulator. As
          of now, that has been my exposure to using “liquid glass”. It seems to
          have gotten quite some attention during this past WWDC week.
        </p>
        <p>
          As a whole, with how Apple has trended towards a “smaller window”
          design across all of their platforms I think this aesthetic makes
          sense. It looks refreshing and the visuals are done well, but of
          course
          <a href="https://www.reddit.com/r/UXDesign/comments/1l7fexe/apples_new_liquid_glass_ui_doesnt_look_accessible/">a bit more work</a>
          seems to be needed here. Pure navigation elements also take up less
          screen space meaning more room for the actual screen content, which is
          generally a good thing. Overall, it’s new and fun to use, though I’m
          sure that feeling will eventually wear off over time.
        </p>
        <p>
          However, nothing really fundamental changed here. Notes still lets you
          write notes, photos still lets you view photos, camera still lets your
          shoot photos and video. The overall navigation of these apps may have
          improved, and thus have become “easier to use”. Virtually all of the
          same features exist in each of these apps, and none of them have been
          changed substanially outside of these navigation concerns.
        </p>
        <p>
          Making an app look visually pleasing, “easy to use”, and “accessible”
          are only some of the more superficial concerns of an app’s overall
          design. They are a neccessary evil to represent and obtain the "real
          value" of software. You can have all 3 of those things, but still have
          a terrible overall experience if your app doesn’t provide "real
          value”. “Real value” generally comes from providing the information
          necessary to answer the user’s questions and help to guide them make
          decisions on navigating their life.
        </p>
        <p>
          For instance, take an empty day on the newly liquid glassified
          calendar app.
          <img src="./liquid-glass/assets/calendar-1.jpeg" alt="An empty day on the newly designed iPadOS calendar app depicting Friday, June 20th, 2025.">
          It’s clear that I have no events planned for this day, and that the
          date of the day is Friday, June 20th, 2025. Unfortunately, that’s all
          the information I get. Other events planned on the other days of the
          week are not hinted at or shown in the week day tab view, so all the
          useful information I get can only be events scheduled on Friday, June
          20th, 2025.
        </p>
        <p>
          Some very important questions that I may have as a user are left
          unanswered:
        </p>
        <ul>
          <li>
            What the heck should I do today after work or when I have free time?
          </li>
          <li>
            This is a Friday, so should I decide to have some fun with friends
            before the weekend?
          </li>
          <li><del>Should I deploy my app to production?</del></li>
          <li>
            If I decide to “go out” tonight, how will that impact the growth of
            my side business that I’ve been working on?
          </li>
          <li>
            If I’m physically active, what kind of workout should I do today?
            Should I take a rest day?
          </li>
          <li>
            What is the weather going to be on this day? Will it affect what I
            have in mind?
          </li>
          <li>etc.</li>
        </ul>
        <p>
          Calendar doesn’t help me answer these questions in any way shape or
          form. If I were a daily user of the calendar app, I’m almost certain
          it would be possible for the app to use my past data to help me answer
          such questions.
        </p>
        <p>
          Many say that they use a calendar to “plan” or “organize” their day,
          almost like they “command” events to happen at certain times of the
          day. This true to the extent that the visual representations of those
          events in the calendar app have been <em>understood</em> as the events
          that an individual will likely attend to in real life. However, the
          key point is “understanding”, that’s the end goal “planning” or
          “organzing”.
        </p>
        <p>
          For instance, take this slice of a particular day.
          <img src="./liquid-glass/assets/calendar-2.jpeg" alt="A view of part of my day on Tuesday, June 10th, 2025 on the new iPadOS calendar app.">
          Here, I’m attending an all day event known as “CommunityKit”. Though,
          I also have some other events planned such as a morning workout, the
          act of driving from where I live to the location of all the WWDC
          events, and a meetup with a friend.
        </p>
        <p>
          This view shows me visuals of the times and durations of each event
          throughout the day, and <em>correlates</em> them by showing a vertical
          overlapping list with the list markers being the hours of the day.
          When I add a new event to this day, I can visually see the time it
          occurs in relation to other events in my day. This in turn, allows me
          to <em>understand</em> when and where I might plan another event. The
          calendar view correlates an event with time, and where it is
          temporally located in relation to other events. Therefore, I am now
          equipped with more information to make a decision on where and when to
          schedule my next event during the day.
        </p>
        <p>
          On it’s own, the sequence of events you attend to during a day is a
          heavily abstract systemic model that’s hard to visualize, which is why
          the concept of a calendar was invented. A calendar on its own is a
          tool to visualize such an abstract data model, allowing you to
          organize your day with it to better <em>understand</em> your day. By
          contrast, the calendar itself is not a control center for “planning”,
          though it may seem like it when describing it to others. Instead, the
          calendar collaborates with you by giving you the information necessary
          to come to an <em>understanding</em> of what how your day looks like.
        </p>
        <p>
          In today’s digital medium, and with the available hardware, more
          correlations and questions can be answered about a user’s day than a
          traditional physical calendar. A physical calendar cannot read and
          understand past events, cannot understand the user’s goals, cannot aid
          in the process of organization, cannot read possible future
          world-events and tell the user to prepare, etc. No amount of “liquid
          glass” will draw more of those correlations.
        </p>
        <p>
          Generally, the amount of discussions on how to best answer the user’s
          questions through design seems to be far lower than the discussions on
          whether or not liquid glass looks visually pleasing or is
          “accessible”. If a user cannot navigate or appreciate the visual
          design, then ultimately their questions will be left unanswered, but
          that is only an aspect of why we actually use software.
        </p>
        <p>— 6/14/2025</p>
        <p>
          PS. The liquid glass effects in this article were replicated from
          <a href="https://github.com/bergice/liquidglass">here</a>. Thanks!
        </p>]]></content:encoded>
    </item>
    <item>
      <title>Command Handlers Don&apos;t Scale</title>
      <link>https://whypeople.xyz/command-handlers-dont-scale</link>
      <guid isPermaLink="true">https://whypeople.xyz/command-handlers-dont-scale</guid>
      <pubDate>Sun, 08 Jun 2025 00:00:00 GMT</pubDate>
      <description>Technology, organizations, and societies need to be collaborative, and not enable their own destruction by refusing to question their own requirements.</description>
      <content:encoded><![CDATA[<h1 id="command-handlers-don-t-scale">Command Handlers Don’t Scale</h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          Commands are a relatively simple concept that we have an intuition of
          in society. If I command you to perform some action, it is therefore
          socially acceptable for you to carry out that action in the manner I
          commanded you to do it.
        </p>
        <p>
          As a society we’ve used this technique in many large-scale technical
          and societal systems designs over the course of many centuries. Often,
          the results have been far less than ideal, and have caused stagnation
          or even degredation for those involved.
        </p>
        <p>
          In fields like programming, the concept of a command handler may have
          a formal definition depending on the context within the field (eg.
          CQRS or implementing a formal interface named
          <code>XXXCommandHandler</code>). In some senses, such definitions may
          play into the larger abstract ideas of what a command handler means in
          the grand scheme of things, but inherently they are not the focus of
          this article.
        </p>
        <p>
          Programming is certainly one of the many domain examples we’ll cover,
          as I imagine it largely would correlate with the audience. However,
          it’s apparent that we see the root systemic principle behind the
          notion of “commanding” something, and why it faces scaling issues if
          we are to understand how new systems should behave. This involves
          looking beyond just the programming domain, and requires us to look
          into other adjacent and non-adjacent domains such as UI/UX design,
          corporate structure, and forms of government. Either in shape or form,
          all of these domains carry major systems design issues around the
          notion of command handling.
        </p>
        <p>
          We’ll start by defining what commands and command handlers are. Then,
          we’ll look at how such principles violate the laws of scaling, and how
          they are poorly used in existing domains. Afterwards, we’ll touch upon
          a new principle that has been used in many large-scale systems present
          today, and why it can combat many of the scaling issues that command
          handling faces. In doing so, I’ll cover a few ways to restructure
          command handling systems to follow this new principle. Lastly, we’ll
          touch upon the notion of when and where command handling should be
          used in conjunction with the newer principle.
        </p>
        <p>
          This article will largely have a broad focus on many domains, as such
          deeper dives into specific domains may not be fully addressed.
        </p>
         <br>
        <h2 id="defining-commands-and-command-handlers">
          Defining Commands and Command Handlers
        </h2>
        <p>
          We can start quite small when thinking about the notion of commands.
          In fact, the first line of code that programmers often write is
          something along the lines of.
        </p>
        <pre><code class="language-javascript">console.log("Hello World")</code></pre>
        <p>
          In this case, our small system is commanding the operating system to
          print <code>"Hello World"</code> to standard output.
        </p>
        <p>
          As one progresses in their programming journey, they may learn about
          something called a function, or possibly as something similar with a
          different name. It turns out that these function things are pretty
          useful for reusing code.
        </p>
        <pre><code class="lang-js">
let number = 0

const increment = () =&gt; {
  number++
}

increment()
increment()
console.log(`The number is now ${number}!`)
        </code></pre>
        <p>
          However, once again we can see that these function things have doubled
          as commands. Now the system has a concept of an incrementing command.
          When the command is imposed on the number, it will be incremented with
          no objections.
        </p>
        <p>
          The “no objections” part is key here, as it implies an
          <em>order that must be enacted</em>. Such an order is therefore a
          command, and the one who carries out such orders is a command handler.
          In this article, we’ll also refer to the actor that creates commands
          as a commander.
        </p>
        <p>
          The requirements for your current project at work may be something
          that you must carry out at all costs. A rule of law that is imposed by
          a government is also something that must be adhered to at all costs.
          The order that a user placed using the Uber Eats app is also something
          that must be carried out at all costs. In all cases, these orders,
          laws, and requirements are defined as commands, those who carry them
          out are the command handlers, and those who create the commands are
          the commanders.
        </p>
        <br>
        <h2 id="scaling-issues">Scaling Issues</h2>
        <p>
          So, <strong><em>why don’t command handlers scale</em></strong>?
        </p>
        <p>
          First, we should be careful to use the term “scale” in the general
          sense. That of a term that refers to increasing or enlarging the
          dimensions of a system.
        </p>
        <p>
          For command handlers, we can generally think of those dimensions as
          the number of commands to handle, and the different variants of the
          commands to handle. Therefore, a command handler operating at scale
          generally has to deal with both a higher volume of many varied
          commands than a handler operating at less of a scale. For increasingly
          enlarging systems, this often means that an inconceivable volume of
          command combinations need to be handled at scale.
        </p>
        <p>
          This higher volume of commands and command variants stems from more
          and different “commanders” invoking the command handler than before.
          Combinations of commands can be formed by the ordering in which
          different commanders send commands.
        </p>
        <p>
          In programming, this could mean calling a set of 100 functions in
          different combinatorical orders. In a company, this could mean many
          different product teams or departments imposing requirements on a
          single product team. In government, this could mean imposing many
          different laws and regulations on its citizens. In all of these cases,
          the volume of commands and their combinations are incredibly hard to
          predict and regulate.
        </p>
        <p>
          For such a dynamic to work properly, the commander has to
          <em>trust</em> that they are properly commanding the handler, and
          likewise the handler has to <em>trust</em> that they are being handed
          the right commands. If any single party breaks this bidirectional
          notion of <em>trust</em>, bad things often happen.
        </p>
        <p>
          In programming, if a set of functions are called in the wrong order,
          bugs occur. In a company, if a product team receives unattainable,
          ambiguous, or conflicting requirements with no room for negotiation
          from different departments, confusion and stress occurs. In
          government, if the rules of law it imposes on its citizens interfere
          with their civil rights, it becomes authoritarian, and invigorates its
          citizens.
        </p>
        <p>
          At increasingly larger scales, the volume and variations of commands
          becomes increasingly harder to understand. Therefore, could you really
          <em>trust</em> in those cases that both parties work together
          correctly?
        </p>
        <p>
          The evidence has shown this to generally be a resounding “no” in many
          systems. Of course, parties with better chemistry may handle
          increasing scales more effectively, but no one can handle infinity.
          Yet, in practice we can often find many cases where this is violated.
        </p>
        <h3 id="violations-in-code">Violations in Code</h3>
        <p>
          In the “real world”, we’ll often encounter plain data non-encapsulated
          structures, even in so-called object oriented languages. For instance,
          this is a very common style of “object-oriented” Java code is not only
          taught to beginners learning OOP, but also common in many Java
          applications. We’ll ignore tools like Lombok that automatically
          generate lots of this code in Java. In fact, I would wager Lombok’s
          existence is more of larger symptom problem of this style, as the
          problems of this code are heavily related to “setters”.
        </p>
        <pre><code class="lang-java">
public class Item {
  private String name;
  private int quantity;

  public Item(String name, int quantity) {
    this.name = name;
    this.quantity = quantity;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getQuantity() {
    return this.quantity;
  }

  public void setQuantity(int quantity) {
    this.quantity = quantity;
  }
}
        </code></pre>
        <p>
          The use of getters and setters in this case is often taught as a form
          of “encapsulation”.
        </p>
        <p>
          However, this <code>Item</code> class is more like a C data structure,
          and not really an “encapsulated object”. In effect, you can consider
          this class as a dumb command handler since it sets whatever values are
          passed to its setters. In turn, it doesn’t know anything about those
          values or where they come from, therefore it must <em>trust</em> that
          the incoming values are valid and have good faith. Due to this, we
          easily end up with a limitless possible combinations of
          <code>Item</code>s in our system, and thus we propagate the complexity
          of what constitutes a “valid” Item throughout our entire system.
        </p>
        <p>
          For instance, a negative quantity doesn’t really make sense on an
          <code>Item</code> in many systems. With this so-called “encapsulated”
          <code>Item</code> object, the rest of the system has to
          <em>trust</em> that the quantity has not been set negative somehow in
          some galaxy far-far away.
        </p>
        <p>
          If we’re lucky, there may exist some class called
          <code>ItemValidator</code> somewhere else in the system that ensures
          the quantity is not negative, but often this class does not exist
          leaving the code that leverages Items to perform this validation.
        </p>
        <pre><code class="lang-java">
public class ItemValidator {
  public boolean validate(Item item) {
    return item.getQuantity() &gt;= 0;
  }
}
        </code></pre>
        <p>
          Even with this class, the knowledge about how an
          <code>Item</code> works has been spread around to different components
          of the system instead of being “encapsulated” within the bounds of
          <code>Item</code>. Different components of the system now must
          <em>trust</em> that every <code>Item</code> has gone through the
          validator (and at the corect time) somehow. Every future change to the
          item quantity will also need to be accompanied by a call to the
          <code>ItemValidator</code> in order to ensure its validity. Yet we’ve
          only added more complexity to our system because now we must consider
          both an <code>Item</code> and <code>ItemValidator</code> to be in
          tandem with one another.
        </p>
        <p>
          Unfortunately, we often aren’t so lucky in the real world, and the
          intimate knowledge about an <code>Item</code> is often spread out
          through multiple unrelated components of the system. If we need to
          update the intimate knowledge of an item, we must now ensure that we
          make <em>trustworthy</em> updates to all of those unrelated components
          in the system. This is especially error-prone in the significant
          number of systems that have little or no form of automated/rapidly
          available testing pipelines.
          <img src="./command-handlers-dont-scale/assets/item-service.png" alt="The class hierarchy of an ItemService that contains many typical classes that you would see in a Spring application, all depending on the Item class.">
        </p>
        <p>
          Even worse is when the intimate knowledge of <code>Item</code> has to
          be distributed to multiple nodes of a distributed system, as is the
          case with even simple web-applications due to the “frontend” and
          “backend” distribution. Unfortunately, Java does not have a notion of
          sending “objects” across the internet, and therefore we have to
          <em>trust</em> each node of our distributed system to have the correct
          intimate knowledge of <code>Item</code> (ie. each node in the system
          has to separately redefine the <code>Item</code> type somehow with all
          of its validation rules).
        </p>
        <p>
          To visualize this.
          <img src="./command-handlers-dont-scale/assets/item-system.png" alt="Many different company services written in different languages that each have to define their own Item class and validation logic for the item.">
          Each node of this system needs to define an <code>Item</code> class
          somehow, and then also needs to replicate the validation logic for an
          item. However, this replication means that each node has to agree on
          what an <code>Item</code> is and how to validate it. If the semantics
          and notions of a valid <code>Item</code> were to change in the future,
          each node in this system would have to independently update its
          representation of the <code>Item</code> class in order for the system
          to be stable (ie. Each node would have to trust the nodes it interacts
          with handle the notion of an <code>Item</code> correctly).
        </p>
        <p>
          Naturally, this is quite error prone, and can be especially tedious
          when many different kinds of people are involved in the maitenance of
          this system. Some of those “20 other company services” may be written
          in Java, and can therefore reuse the <code>Item</code> class if it is
          moved into a shared library. However, this would then tie much of the
          system to Java itself, and Java would eventually become the default
          choice whenever possible for new nodes in this system. If another
          language would be more suitable for a specific node in this system
          (eg. Java generally isn’t so great at making iOS applications), then
          we repeat the manual replication problem.
        </p>
        <h3 id="violatons-in-governments">Violatons in Governments</h3>
        <p>
          As an ideally moral species, our scaling of society needs to handle
          and provide hospitality for ~8 billion human beings. We have failed at
          this so far, though it seems our methods may have improved since the
          invention of the modern state.
        </p>
        <p>
          If we think about governments as the “commanders” and the populace as
          the “command handlers”, we can infer that any passed law/executive
          order/ruling is effectively a command on the populace. With an
          absolute monarchy the monarchs commands are above all else, and
          therefore the populace has to <em>trust</em> that the commands of 1
          individual are in good faith. As history shows, this approach
          generally has not scaled well, and many revolutions were fought to get
          rid of it. These days developed nations have better systems of checks
          and balances in place that give the populace more power against bad
          commands.
        </p>
        <p>
          Yet, many of the governments in today’s world still operate as
          “commanders”, and individuals still lust for power even in developed
          nations with checks and balances. The notion of what those checks and
          balances are must be interpreted and acted upon by those in power,
          whose interpretations may have negatively diverted from the original
          intent of those checks and balances. Such is often the case when a
          single political party holds a super-majority rule in each branch of
          government.
        </p>
        <h3 id="violations-in-business">Violations in Business</h3>
        <p>
          Additionally, some may argue there exists a similar power dynamic in
          many companies. If we look at a typical corporate structure, there
          must be at least 1 person who can make decisions that impact the
          significance of the company, the CEO. At the lowest level of this
          structure are often the grunts who in the worst cases are pure command
          handlers that must only fulfill spelled-out orders from management
          with no exceptions. In software development, these people may be
          called “code monkeys”, as their only job is to write the code and not
          much of anything else. Often this causes employee burnout, and as a
          result the product is typically less innovative.
        </p>
        <p>
          If a company wants be successful, it needs to find its customers,
          attend to the wants and needs of those customers, and operate in such
          a way that it gains resources to produce more output. Customer needs
          and wants depend on the domain itself, market factors, the
          demographics in said market, and the individual people’s contexts
          themselves. Operational needs includes RND costs, timelines,
          communications, decision making processes, revenue streams, etc. For
          each of these, appropriate domain expertise allows better decisions to
          be made quicker in each of these areas.
        </p>
        <p>
          If product decisions are made without input from those who own the
          capabilities to create the product, then those who create must trust
          the limited expertise of management above them. Simultaneously, if the
          power is solely given to the creators, then management must trust the
          limited business expertise of the creators when making product
          decisions. Either is a recipe for disaster, as one group commands the
          other in some form.
        </p>
        <p>
          The company itself is a system that lives within a larger economic
          system, and its role is ultimately to produce value in the said larger
          economic system. Therefore, a successful company system must be
          carefully designed with the factors at play in the larger economic
          system that can change with time and growth.
        </p>
        <p>
          Smaller companies often succeed by being the best in a specific
          domain, and larger companies can succeed by playing in domains that
          are not accessible to smaller companies. In both cases, the
          appropriate systems designs are dependent on the ability of domain
          experts to innovate in their domains, and on the business to produce
          customer value in those domains.
        </p>
        <p>
          With such a duality, those who control business, and those who control
          the domain expertise both need the power to say “no”.
        </p>
        <h3 id="violations-in-user-interfaces">
          Violations in User Interfaces
        </h3>
        <p>
          Modern UIs of this day are almost textbook definitions of command
          handlers. You open an app, you press a button, and therefore you
          expect the app to do what you want. If the app fails this task, you
          often become an angry user even if you commanded the app to do
          something malicious for yourself (eg. A morbidly obese person getting
          mad at a food delivery app when it fails to order their next unhealthy
          meal.). Due to the prevalence of “apps” in our society, this kind of
          command handling treatment has now been baked into our natural
          intuitions, and thus deviating from it poses a significant design
          challenge.
        </p>
        <p>
          As designers, we like to talk in terms of “conversions”, “liquid glass
          rectangles”, and “ease of use”. The UI must “get out of the way” at
          all costs to ensure that users can command a product to their liking.
          Their desires are above all else, and we must ensure that a product is
          “simple and easy to use” to ensure their desires are met. If their
          desires are met, we often have a higher “conversion rate”, and thus
          the company achieves growth. Therefore, we’ll design our UI to be a
          kind of control panel that only presents the means of achieving those
          desires.
        </p>
        <p>
          <strong><em>But do we dare question those desires?</em></strong>
        </p>
        <p>
          Should we allow a morbidly obese person to order unhealthy meals from
          their bed using an app? Should we allow someone who’s addicted to
          social media (if they don’t use it for healthy reasons) to
          “doomscroll” for many hours per day? Should habit tracking apps allow
          one to track many habits at once, when they can’t even stick to one?
          etc.
        </p>
        <p>
          Making an app look beautiful and simple/easy to use is only a fraction
          of the battle when it comes to tackling real-world issues. If an app
          is designed as a simple command center to perform an action, then we
          end up with scenarios like those in the questions above. A command
          center app must trust that the user’s intentions are in good faith in
          order to provide value, and often they are not.
        </p>
        <p>
          An individual who uses social media to promote their brand
          fundamentally has different needs than someone who’s addicted to
          social media in an unhealthy way. While business metrics views may be
          provided for the influencer, the addicted user often gets no feedback
          on their negative usage from their app of choice. Even worse, a common
          issue that affects nearly all demographics on social media relates to
          certain content triggering poor physcological and physiological
          responses. Social media apps generally do not provide insights as to
          what kind of content causes mental health degredation on a per-user
          basis.
        </p>
        <p>
          In more modern times, many applications involve the use of an LLM that
          enacts commands on behalf of the user. These commands, structured as
          natural language prompts, are passed to an LLM which then calls into a
          set of tools to automate a task or workflow (such as coding). The
          result is the ability to “create more things” in a shorter time frame,
          which on its own isn’t problematic. Rather, the LLM tends to
          <em>take away</em> the understanding of <em>how</em> something is
          created in the first place because the user <em>trusted</em> the LLM
          with their commands, and the LLM <em>trusted</em> that the user’s
          commands were in good faith by carrying them out with no objections.
          When it comes time to maintain or evolve initial creations, problems
          often arise due to this lack of understanding (eg. why many vibe
          coding attempts fail).
        </p>
         <br>
        <h2 id="another-way-of-thinking">Another Way of Thinking</h2>
        <p>
          The first and total opposite mindset shift we can have is to trust
          absolutely nothing. As commanders, we would therefore have to stop
          sending any commands. As handlers, we would reject every command sent
          our way. Naturally, life forms could not exist this way, as everything
          would isolate itself from one another, and therefore die out.
        </p>
        <p>
          Clearly, there has to be a base level of trust somewhere, and in
          general we command things because we want things to happen. However,
          we also are sometimes aware that our commands cannot or should not be
          fulfilled. In other words, if we want to scale this thinking, we
          really should be “requesting” things to happen, and not “commanding”
          them. This way, we unlock the ability to collaborate with the request
          handler.
        </p>
        <p>
          Ideally, when we request something from the handler, the handler
          should have the ability to question why we sent the request in the
          first place. After all, it is a “request” and not a “command”. The
          handler may even choose not to respond if such a response isn’t
          necessary. Since we’re merely requesting, we should have an intuitive
          notion that our request may not be fulfilled in the way we expect, and
          therefore be prepared to deal with the consequences.
        </p>
        <h3 id="the-internet">The Internet</h3>
        <p>
          In fact, this request-response model scales quite well, because it
          happens to be the model that the internet runs on. Not once in the ~60
          year history of the internet has it ever been completely brought down,
          and the network itself has expanded to trillions of nodes. While parts
          have fallen down, and still fall on a regular basis, there exists
          enough redundancy to not bring the entire operation to a halt.
        </p>
        <p>
          The model of the internet is quite simple. A client can send a request
          to a particular server for information, or to perform some operation.
          This request must take the form of a stream of bytes, and this allows
          the server to interpret the request however it likes. With protocols
          like TCP, we can even guarantee that this byte stream reaches the
          server, and reaches it in the same order we sent the bytes.
        </p>
        <p>
          The server can then look at its internal state, and process the
          request as it sees fit. It may choose to respond favorably, or it may
          not respond favorably, or it may event not respond at all. If it does
          respond, the response must also be in the form of a byte stream, and
          this allows the client to interpret the response how it likes.
        </p>
        <p>
          The client can then look at its internal state, and processes the
          response as it sees fit. It may choose to send further requests to the
          server, or it may choose to proceed without further communication.
        </p>
        <p>
          Neither the client nor the server need to understand the full context
          of each others’ internal states in this communication, nor can they
          directly access each other’s internal states. To access the internal
          state of the server, the client can only request it in a way that the
          server understands, and even then the server may choose to present it
          however it likes.
        </p>
        <p>
          Neither the client nor the server know the methodologies their
          opposites are using to process the messages sent between themselves.
          This is a notion known as encapsulation.
        </p>
        <h3 id="improving-the-item-example">Improving the Item Example</h3>
        <p>
          We can explore the idea of requests by concretely fixing the item code
          example from earlier. While the mainstream ecosystems of tooling are
          not capable of fully fixing the “knowledge replication” issues we
          faced earlier, we can certainly improve the isolated usage of
          <code>Item</code> on a per-node basis. Fixing the replication problem
          requires a different medium of distributing applications altogether,
          which is best left for a future exploration.
        </p>
        <p>
          It is a fundamental law of <code>Item</code> that does not allow the
          quantity to be negative, so therefore we should encapsulate that
          knowledge into the object. We can use Java’s
          <code>Optional</code> type to indicate that the quantity of an item is
          not valid. Also, for the sake of respecting the request-response model
          more literally, we’ll switch to an immutable design that returns new
          <code>Item</code> instances when updating the item.
        </p>
        <pre><code class="lang-java">
public class Item {
  private String name;
  private int quantity;

  private Item(String name, int quantity) {
    this.name = name;
    this.quantity = quantity;
  }

  public static Optional<item> of(String name, int quantity) {
    if (quantity &lt; 0) return Optional.empty();
    return Optional.of(new Item(name, quantity));
  }

  public String name() {
    return this.name;
  }

  public Item withName(String name) {
    return Item.of(name, this.quantity).get();
  }

  public int quantity() {
    return this.quantity;
  }

  public Optional&lt;Item&gt; withQuantity(int quantity) {
    return Item.of(this.name, quantity);
  }
}
        </item></code></pre>
        <p>
          At the very least, we no longer can set the quantity to any value we
          desire. We can only “request” that the quantity be set, and by using
          <code>Optional</code> we’re forced to expect a potential “bad
          response”. The intimate knowledge of an <code>Item</code> is now
          “encapsulated” within the class itself, so we can get rid of
          <code>ItemValidator</code>. Given that invalid <code>Item</code>s are
          now unrepresentable in our system, we’ve also massively reduced
          systemic complexity as the components dependent on an
          <code>Item</code> have no need to worry about validation.
        </p>
        <h3 id="improving-user-interfaces">Improving User-Interfaces</h3>
        <p>
          A dedicated piece on this topic can highlight far more detail here
          including how AI can most effectively be used in apps, but the
          essentials can be covered briefly.
        </p>
        <p>
          User interfaces best provide value to their users when users form
          connections and learn new things as opposed to simply commanding them.
          We use apps to see what our friends are up to, figure out what our
          plans are for tomorrow, understand how many calories a day we’re
          burning, view political discourse, figure out how to best manage
          finances, learn what our passions are, and to learn how to create
          things. Therefore, it is essential that user-interfaces are designed
          around correlations and relationships in <em>information</em>.
        </p>
        <p>
          For instance, social media apps can alerts users to avoid content that
          knowingly degredates their mental state rather than letting them
          doomscroll. Stress management apps can relate personal actions and
          incidents to long-term progress on reducing stress levels rather than
          focusing on singular moments in isolation. Weight loss apps can help
          users decide on a diet that’s enjoyable, and effective for their
          weight loss goals rather than serving as a simple calorie tracker.
          Before making a post, social media apps can inform the author of how
          it may perform with certain demographics rather than leaving it
          entirely up to the author. Habit tracking apps can help users find the
          right and most motivating habits to focus on, rather than leaving
          users to find that for themselves. Messaging apps can alert users when
          it’s not the best time to contact someone, instead of leaving users to
          determine that themselves.
        </p>
        <p>
          There are infinitely many correlations available to choose from, and
          in an ideal world we wouldn’t be limited to the APIs Apple and Google
          provide in their operating systems for mobile apps. LLMs can also be
          helpful here, but we have to be careful enough to not treat them as
          mere “assistants” or “servants”. (ie. Treating them merely as command
          handlers that act as servants in accordance to the user prompts they
          receive. Often, the prompts themselves need to be questioned.)
        </p>
         <br>
        <h2 id="not-a-silver-bullet">Not a Silver Bullet</h2>
        <p>
          Imagine a world where there was no downside to everyone trusting each
          other to get things done, or rather a world in which command handlers
          scale. Such a world would be massively more efficient than the one we
          live in, because we wouldn’t need to waste time on debating if the
          “commands” were correct in the first place.
        </p>
        <p>
          The problem with a request-response system is overhead. The
          interpretation, internal deliberation process, and fail-safes needed
          to scale the system introduce many inefficiencies that are not related
          to the core functions of the system. This is often why a small startup
          can move faster than a bigger company, or why pure procedural code
          written in C can have better runtime performance than code written in
          higher level “object-oriented” languages.
        </p>
        <p>
          With that in mind, command handlers aren’t inherently bad, it’s merely
          that they have scaling troubles without the overhead of a
          request-response model.
        </p>
        <p>
          As system designers, it is actually our job to deploy that overhead
          correctly. One tool for detecting such a deployment is to look at the
          coupling between components. Coupling in this case differs its
          definition depending on the domain. A small team of people who have a
          high-team chemistry have a high degree of coupling on each other. An
          object that depends on references to many other objects in programming
          is coupled with each of those other objects. Regardless of the domain,
          coupling can be seen as the connections between different systemic
          components.
        </p>
        <p>
          If multiple components are highly coupled with only a few “outside
          connections”, then we can deploy the overhead to the few outside
          connections as to not add overhead to the high-degrees of coupling
          from the inside. This creates an isolated and effective component in a
          specific domain, but adds overhead to prevent other components in
          other isolated domains from dictating its functionality.
        </p>
        <p>
          We may also try to reduce the coupling on the inside as well, but from
          experience that usually only works up to certain point as the reducing
          the coupling may involve the creation of more smaller components. The
          communication overhead of those smaller components can often more
          complex than a singular monolithic component when split incorrectly. A
          great example of this is a small team of a few brilliant people who
          lack team chemistry, as this creates a massive communication overhead
          for each individual in order for the team to function.
        </p>
        <p>
          As long as the bidirectional notion of trust can be upheld, then
          command handling can function effectively. Managing the trust factor
          is therefore a crucial part of designing efficient systems that scale.
        </p>
         <br>
        <h2 id="to-end-with-a-question">To End With a Question</h2>
        <p>
          <em>Do you believe that following the commands of society will produce
            a better world?</em>
        </p>]]></content:encoded>
    </item>
    <item>
      <title>The Root</title>
      <link>https://whypeople.xyz/root</link>
      <guid isPermaLink="true">https://whypeople.xyz/root</guid>
      <pubDate>Sat, 07 Jun 2025 00:00:00 GMT</pubDate>
      <description>A formal declaration of my insanity (AKA my motivations for wanting to write about my work).</description>
      <content:encoded><![CDATA[<h1 id="the-root" class="title">The Root</h1>
        <p class="article-reading-time">-- Minutes to Read</p>

        <p>
          Whether this is the root of all evil, or of something else entirely is
          entirely up to you to decide.
        </p>
        <p>
          In a certain
          <a href="https://www.google.com/search?client=firefox-b-1-d&amp;q=Witch+on+the+Holy+Night">visual novel series</a>, one may terribly try to describe the root as the sun, and this
          would get you certain looks from characters in said series for the
          description. Just a few centuries ago, if you suggested that the sun
          was the root of the solar system you would receive the reward of
          condemnation, and possibly an additional gift of having your
          <a href="https://en.wikipedia.org/wiki/Galileo_Galilei#/media/File:Galileo%E2%80%99s_finger.jpg">middle finger preserved for eternity</a>
          in your honor.
        </p>
        <p>
          With these examples in mind, a logical human can only conclude that
          humans are inherently illogical. What has materialized as “progress”
          in the past few centuries first had to come from millenia of book
          burnings, and largely failing models of civilization. In it’s ~200,000
          year history, only the most recent 400-500 of those years have been
          spent in a world of “modern science”.
        </p>
        <p>
          One may ask why it took so long for the human race to adopt such a
          methodology of “progress”. A century before the Scientific Revolution
          began in the 1500s, the Printing Press came about and allowed for the
          widespread circulation of previously hand written material. Over the
          next few centuries, the literacy rate in Europe grew dramatically, and
          this would eventually lead to the invention and use of the modern
          state in the 1600s and 1700s. The US Constitution was drafted only
          ~350 years after the printing press, and at the time the previously
          established notion of a “monarchy” was now under heavy scrutiny for
          first time in thousands of years.
        </p>
        <p>
          Only ~200 years after the ratification of the US constitution, far
          more mediums that enabled the widespread circulation of ideas had come
          into fruition. Namely, one of those being the internet, and built on
          top of it, the web. We’ve also now somewhat figured out how to
          “emulate” the output of human thought in a digital medium, though it
          seems that much more work is needed in this area.
        </p>
        <p>
          In just 600 years, we’ve largely flipped the structure of society
          upside down, and dispelled many previously conceived notions of
          societal operation. This came about largely due to the ability to
          distribute ideas effectively across long distances, which in turn led
          to the “know-how” to create mediums that further made this process
          more efficient and effective.
        </p>
        <p>
          Yet, this is a movement that is still in its infancy, as our world
          still has a strong notion of a minority of “developed” and a majority
          of “undeveloped” nations. Even in “developed” nations, citizens
          experience problems largely unique (eg. obsesity, mental health
          struggles, etc.) to the nature of the “developments” in their nation.
          Our time period is also likely the first in which we fear that human
          technology can “end the world”. Previously such “world-ending” events
          were largely religious.
        </p>
        <p>
          When a large volume of ideas are circulated in increasingly short time
          spans, it is no doubt that many bad ideas will make their way through
          the mediums, and eventually be implemented in the world somehow. In
          fact, it’s quite common today to hear ideas circulated such as “ideas
          are cheap” or “ideas are a dime a dozen”. This is generally a kind of
          thinking that is oriented towards people building new products or
          services, as much of the burden of doing such a venture is on the
          execution and implementation details. Therefore, “coming up with good
          ideas” is often seen as a waste of time. Of course, such thinking was
          only enabled by systems that enabled universal human literacy in the
          first place.
        </p>
        <p>
          Bad ideas are certainly not cheap when they are successfully
          implemented and widespread. In fact, they are quite expensive in both
          time, human life, and monetary value. Monarchies persisted for
          millenia, and many revolutions in different parts of the world had to
          be fought to dispell them. That only came after the few centuries it
          took to convince a part of the world that they were a bad idea in the
          first place. Of course, wars are quite monetarily expensive as well.
        </p>
        <p>
          In business, the products that best solve their designated problems
          often don’t win, or are never brought into fruition because they have
          no potential at replacing the already established solutions. If
          smaller players cannot dethrone larger players, you get stagnation.
        </p>
        <p>
          Social media apps format all content the same, and provide the same
          basic interaction tools (liking, upvoting, downvoting, commenting) for
          each unit of content. If a unit of content is best represented by
          another format, it cannot be shared on the social media platform. If
          the format’s medium that would best reflect the content is not
          widespread, then the content cannot be expressed in full. If the
          content cannot be expressed in full then many of its ideas are lost,
          and you get stagnation.
        </p>
        <p>
          If you <i>solely</i> rely on the use of AI (or any technology that can
          automate creation) to complete tasks, your ability to complete such
          tasks is inherently tied to the ability of AI. If such a technology
          plateaus, then your ability to complete the task can no longer grow
          whilst you’re solely dependent on AI, and you get stagnation.
        </p>
        <p>
          Thus far, we have quite literally failed at finding a scalable set of
          mediums that addresses the needs of 8 billion (and increasing) people
          on a single planet. This is in spite of the massive hardware and to an
          extent software advantages we have today compared to the last decade.
          If the species were to become multi-planetary, this is an even a
          bigger problem due to the need to understand the even greater number
          of personal contexts that entails. Stagnation is therefore fatal in
          such circumstances.
        </p>
        <p>
          In the near-term, improving our usage of existing and emerging
          technologies can help address these challenges in the products we
          create on a daily basis.
        </p>
        <p>
          As such, I figure it's best that I record my experiences and thoughts
          on developing apps that better enhance content-creation, and address
          many of the public mental and physical health challenges stemming from
          existing apps. In particular, a focus on "enhancement" of innate user
          ability (ie. enhancing their ability to understand themselves and
          navigate the world) is missing from many of today's products.
        </p>
        <p>
          However, in the long-term something greater seems to be needed to
          address the scaling problems we are currently facing. Such requires
          going beyond current societal and industry ideas, and requires
          developing new and qualitatively different systemic foundations.
        </p>
        <p>
          I don't know what the exact answer is at this current moment as to
          what those will look like. However, I believe it's essential that I
          document my findings and experiences to help put into context the
          eventual "understanding" of those ideas.
        </p>]]></content:encoded>
    </item>

  </channel>
</rss>