<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: djuleayo</title>
    <description>The latest articles on DEV Community by djuleayo (@djuleayo).</description>
    <link>https://dev.to/djuleayo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3387115%2Ff5e53385-6f98-47c6-804d-dc4c9692c42a.png</url>
      <title>DEV Community: djuleayo</title>
      <link>https://dev.to/djuleayo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/djuleayo"/>
    <language>en</language>
    <item>
      <title>Why You Must Revisit the Stack Data Structure in the Age of AI</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Sat, 24 Jan 2026 07:22:50 +0000</pubDate>
      <link>https://dev.to/djuleayo/why-you-must-revisit-the-stack-data-structure-in-the-age-of-ai-1ae8</link>
      <guid>https://dev.to/djuleayo/why-you-must-revisit-the-stack-data-structure-in-the-age-of-ai-1ae8</guid>
      <description>&lt;h1 id="why-you-must-revisit-the-stack-data-structure-in-the-age-of-ai"&gt; Why You Must Revisit the Stack Data Structure in the Age of AI   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;p&gt;With the rise of AI-assisted coding, execution has become cheap.&lt;/p&gt; &lt;p&gt;You can let the AI do the wiring.&lt;br&gt; What you &lt;strong&gt;cannot&lt;/strong&gt; delegate is the choice of abstractions.&lt;/p&gt; &lt;p&gt;And that changes everything.&lt;/p&gt; &lt;p&gt;If &lt;em&gt;you&lt;/em&gt; choose the right abstractions, your system scales.&lt;br&gt; If you don’t, you don’t get “bad code” — you get &lt;strong&gt;semantic debt&lt;/strong&gt;:&lt;br&gt; a pile of spaghetti whose behavior you no longer understand.&lt;/p&gt; &lt;p&gt;That’s why data structures matter more today than they did before. Not because you must be able to &lt;em&gt;implement&lt;/em&gt; them, but because you must understand the &lt;strong&gt;semantic consequences&lt;/strong&gt; of choosing one.&lt;/p&gt; &lt;p&gt;Those consequences often cascade far beyond what you intended.&lt;/p&gt; &lt;p&gt;Let’s see this with the simplest example possible.&lt;/p&gt;  &lt;h2 id="the-stack-is-not-just-a-stack"&gt; The Stack Is Not Just a Stack   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;A stack is one of the simplest data structures we have. So simple, in fact, that it exists physically in hardware.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;CPUs implement stack pointers&lt;/li&gt; &lt;li&gt;Operating systems allocate a stack per process&lt;/li&gt; &lt;li&gt;Languages expose it implicitly via function calls and scopes&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;In C-like languages, opening a &lt;code&gt;{}&lt;/code&gt; opens a stack frame. When you exceed stack memory, the OS kills your program. Even &lt;em&gt;Stack Overflow&lt;/em&gt; is named after this exact failure mode.&lt;/p&gt; &lt;p&gt;You &lt;strong&gt;cannot&lt;/strong&gt; write a program without a stack.&lt;/p&gt; &lt;p&gt;So far, nothing surprising.&lt;/p&gt; &lt;p&gt;But now track execution &lt;em&gt;mentally&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;If function &lt;code&gt;A&lt;/code&gt; calls &lt;code&gt;B&lt;/code&gt;, and then &lt;code&gt;C&lt;/code&gt;, execution looks like this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Enter &lt;code&gt;A&lt;/code&gt;
&lt;/li&gt; &lt;li&gt;Enter &lt;code&gt;B&lt;/code&gt;
&lt;/li&gt; &lt;li&gt;Exit &lt;code&gt;B&lt;/code&gt;
&lt;/li&gt; &lt;li&gt;Enter &lt;code&gt;C&lt;/code&gt;
&lt;/li&gt; &lt;li&gt;Exit &lt;code&gt;C&lt;/code&gt;
&lt;/li&gt; &lt;li&gt;Exit &lt;code&gt;A&lt;/code&gt;
&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;At runtime, the stack is a &lt;strong&gt;path&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;But over time, the trace of execution forms a &lt;strong&gt;tree&lt;/strong&gt;:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;
&lt;code&gt;A&lt;/code&gt; is the parent&lt;/li&gt; &lt;li&gt;
&lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; are siblings&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The stack is not “just LIFO”. It is a traversal of a tree-shaped execution history.&lt;/p&gt; &lt;p&gt;This matters more than most developers realize.&lt;/p&gt;  &lt;h2 id="execution-leaves-a-shape"&gt; Execution Leaves a Shape   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Most developers are comfortable with stack behavior.&lt;br&gt; Far fewer are comfortable with &lt;strong&gt;closures&lt;/strong&gt; — and this is why.&lt;/p&gt; &lt;p&gt;Closures break the illusion that execution is only a stack.&lt;/p&gt; &lt;p&gt;Consider this example:&lt;/p&gt; &lt;div class="highlight js-code-highlight"&gt;
&lt;pre&gt;&lt;code data-lang="ts"&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt; makeClosure &lt;span&gt;=&lt;/span&gt; () &lt;span&gt;=&amp;gt;&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;let&lt;/span&gt; a &lt;span&gt;=&lt;/span&gt; &lt;span&gt;10&lt;/span&gt;; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;function&lt;/span&gt; closure() { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; console.log(a); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; } &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;return&lt;/span&gt; closure; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt; myClosure &lt;span&gt;=&lt;/span&gt; makeClosure(); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;myClosure(); &lt;span&gt;// prints 10 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here’s the puzzle:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;makeClosure() creates a stack frame&lt;/li&gt; &lt;li&gt;That stack frame exits&lt;/li&gt; &lt;li&gt;Yet a is still accessible later&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;So where is &lt;code&gt;a&lt;/code&gt;?&lt;/p&gt; &lt;p&gt;It cannot be on the stack. The stack frame is gone.&lt;/p&gt; &lt;p&gt;The only possible conclusion:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;execution is no longer a simple stack.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;What actually happens is this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The stack is one branch of a larger execution tree&lt;/li&gt; &lt;li&gt;Closures preserve parts of that tree after the branch collapses&lt;/li&gt; &lt;li&gt;Variables escape time, not scope&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Once you understand this, async functions, promises, callbacks, and event-driven runtimes stop feeling magical.&lt;/p&gt; &lt;p&gt;They are not “stack tricks”. They are graph-shaped execution.&lt;/p&gt; &lt;h2 id="why-you-should-care"&gt; Why You Should Care   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;In an age where any of us can spin up a workforce in the form of AI agents, the real challenge is no longer writing code — it’s &lt;strong&gt;scaling systems&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;People sense this intuitively. That’s why they keep building frameworks, layers, and tooling with increasing internal complexity.&lt;/p&gt; &lt;p&gt;Frameworks consume your code — or AI-generated wiring — and impose their own abstractions and limitations. That is precisely where architectural decisions become irreversible.&lt;/p&gt; &lt;p&gt;Here’s the key point:&lt;/p&gt; &lt;p&gt;Seeing the stack as the &lt;strong&gt;active trunk of an execution tree&lt;/strong&gt; is the mental model behind virtual machines, language runtimes, and frameworks themselves.&lt;/p&gt; &lt;p&gt;If you don’t understand the semantic consequences of your data structures, you are outsourcing architecture to tools that don’t pay the cost when things go wrong.&lt;/p&gt; &lt;p&gt;I’ll write more about this using concrete examples from a VM I built using exactly this perspective. As a preview: it supports async pipelines natively — but more on that later.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dynamically Generated Languages vs MCP Servers</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Sun, 18 Jan 2026 15:10:43 +0000</pubDate>
      <link>https://dev.to/djuleayo/dynamically-generated-languages-vs-mcp-servers-53j2</link>
      <guid>https://dev.to/djuleayo/dynamically-generated-languages-vs-mcp-servers-53j2</guid>
      <description>&lt;h1 id="dynamically-generated-languages-solve-the-same-class-of-problems-as-mcp-servers--and-do-it-better"&gt; Dynamically Generated Languages Solve the Same Class of Problems as MCP Servers — and Do It Better   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;p&gt;After battle-testing these ideas in practice, here is my conclusion:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Centralized interaction and intent architectures are inevitable.&lt;/strong&gt;&lt;br&gt; The only real question is whether we build them as &lt;em&gt;formal systems&lt;/em&gt; or &lt;em&gt;probabilistic agents&lt;/em&gt;.&lt;/p&gt;  &lt;h2 id="from-clicking-and-scrolling-to-invoking-capabilities"&gt; From Clicking and Scrolling to Invoking Capabilities   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;User interaction is moving away from clicking and scrolling and toward &lt;strong&gt;invoking capabilities directly&lt;/strong&gt;, most notably via voice.&lt;/p&gt; &lt;p&gt;This shift is powerful:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;If you can name a capability, you can invoke it&lt;/li&gt; &lt;li&gt;Interaction becomes direct&lt;/li&gt; &lt;li&gt;UX complexity drops dramatically&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;From a UX-complexity standpoint, this is a clear win.&lt;/p&gt; &lt;p&gt;However, this change forces a &lt;strong&gt;fundamental architectural shift&lt;/strong&gt; compared to traditional frontend setups.&lt;/p&gt;  &lt;h2 id="react-and-the-problem-of-distributed-capabilities"&gt; React and the Problem of Distributed Capabilities   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Consider a typical React application.&lt;/p&gt; &lt;p&gt;In React:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Capabilities are exposed through &lt;strong&gt;components&lt;/strong&gt;
&lt;/li&gt; &lt;li&gt;Components expose behavior via &lt;strong&gt;props and callbacks&lt;/strong&gt;
&lt;/li&gt; &lt;li&gt;Capabilities are therefore &lt;strong&gt;distributed by construction&lt;/strong&gt;
&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;There is no centralized place where “what the system can do” exists.&lt;/p&gt; &lt;p&gt;Trying to centralize this in React quickly reveals deep friction:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Hooks produce &lt;strong&gt;unstable references&lt;/strong&gt; across renders&lt;/li&gt; &lt;li&gt;Central registries must constantly re-register handlers&lt;/li&gt; &lt;li&gt;Context-based solutions become brittle and stateful&lt;/li&gt; &lt;li&gt;Unsubscribing DOM listeners requires the &lt;strong&gt;exact same function reference&lt;/strong&gt;
&lt;/li&gt; &lt;li&gt;That reference is often no longer available or stable&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Yes, you &lt;em&gt;can&lt;/em&gt; build a capability registry on top of React.&lt;/p&gt; &lt;p&gt;But it is:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Clunky&lt;/li&gt; &lt;li&gt;Hard to reason about&lt;/li&gt; &lt;li&gt;Tightly coupled to rendering lifecycle&lt;/li&gt; &lt;li&gt;Architecturally inverted&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;All of this is a strong signal:&lt;br&gt; &lt;strong&gt;centralized capability invocation does not belong inside the UI layer.&lt;/strong&gt;&lt;/p&gt;  &lt;h2 id="centralization-is-not-optional"&gt; Centralization Is Not Optional   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Now compare this with a system like the Visual Studio Code command palette.&lt;/p&gt; &lt;p&gt;VS Code exposes:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A centralized&lt;/li&gt; &lt;li&gt;Runtime-configurable&lt;/li&gt; &lt;li&gt;Discoverable&lt;/li&gt; &lt;li&gt;Uniform command surface&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Once a command is registered:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;It does not matter where it lives&lt;/li&gt; &lt;li&gt;It does not matter which UI triggered it&lt;/li&gt; &lt;li&gt;It can be invoked uniformly&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This is not an accident.&lt;br&gt; It is a &lt;strong&gt;capability-first architecture&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="the-natural-conclusion-a-decoupled-capability-layer"&gt; The Natural Conclusion: A Decoupled Capability Layer   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;What naturally follows is a module that:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Is decoupled from the frontend&lt;/li&gt; &lt;li&gt;Has no dependency on UI frameworks&lt;/li&gt; &lt;li&gt;Accepts voice or textual input&lt;/li&gt; &lt;li&gt;Emits discrete, authorized commands&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;At first glance, this smells like parsing.&lt;/p&gt; &lt;p&gt;And that is exactly what it is — with one important caveat.&lt;/p&gt;  &lt;h2 id="this-is-parsing--not-ai"&gt; This Is Parsing — Not AI   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Traditional parsing assumes:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Full input available upfront&lt;/li&gt; &lt;li&gt;A lexing phase&lt;/li&gt; &lt;li&gt;A parsing phase&lt;/li&gt; &lt;li&gt;A final AST&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;User interaction does not work this way.&lt;/p&gt; &lt;p&gt;For voice and interactive input we need:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Incremental parsing&lt;/li&gt; &lt;li&gt;Partial feedback&lt;/li&gt; &lt;li&gt;Real-time guidance&lt;/li&gt; &lt;li&gt;Asynchronous evaluation&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This is not AI.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;This is asynchronous parsing.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;That distinction matters.&lt;/p&gt;  &lt;h2 id="mcp-servers-and-the-core-problem"&gt; MCP Servers and the Core Problem   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;MCP servers attempt to solve a similar problem:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Centralized capability invocation&lt;/li&gt; &lt;li&gt;Access via natural language&lt;/li&gt; &lt;li&gt;Location-agnostic execution&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;In practice:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Once a capability is integrated, its origin no longer matters&lt;/li&gt; &lt;li&gt;If you can name it, you can invoke it&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;On the surface, this sounds ideal.&lt;/p&gt; &lt;p&gt;It is not.&lt;/p&gt;  &lt;h2 id="why-informal-invocation-is-dangerous"&gt; Why Informal Invocation Is Dangerous   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;MCP-style systems rely on:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;NLP → mapping informal language to formal side effects&lt;/li&gt; &lt;li&gt;Agents → which can and do hallucinate&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This creates a fundamental mismatch:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Informal input&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Formal consequences&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The moment real side effects exist, you must introduce:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Authorization&lt;/li&gt; &lt;li&gt;Capability scoping&lt;/li&gt; &lt;li&gt;Denial rules&lt;/li&gt; &lt;li&gt;Auditability&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;At that point, you are already rebuilding a &lt;strong&gt;formal system&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="languages-are-formal-systems"&gt; Languages Are Formal Systems   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Languages:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Are minimal&lt;/li&gt; &lt;li&gt;Are precise&lt;/li&gt; &lt;li&gt;Can be syntactically close to natural language&lt;/li&gt; &lt;li&gt;Support deterministic navigation&lt;/li&gt; &lt;li&gt;Enable runtime introspection (autocomplete, discovery)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Most importantly:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Languages allow relational identification.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Objects are identified not by name alone, but by their position in a capability topology.&lt;/p&gt; &lt;p&gt;This is strictly stronger than nominal invocation.&lt;/p&gt;  &lt;h2 id="relational-navigation-beats-nominal-invocation"&gt; Relational Navigation Beats Nominal Invocation   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;LLMs are good at resolving names.&lt;/p&gt; &lt;p&gt;They are bad at:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Systematic search&lt;/li&gt; &lt;li&gt;Relational exploration&lt;/li&gt; &lt;li&gt;Topological disambiguation&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;A formal language enables:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Search through capability graphs&lt;/li&gt; &lt;li&gt;Context-aware narrowing&lt;/li&gt; &lt;li&gt;Progressive discovery&lt;/li&gt; &lt;li&gt;Authorization by construction&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This is not “autocomplete”.&lt;/p&gt; &lt;p&gt;This is &lt;strong&gt;relational observation over a formal structure&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="the-implementation-is-already-known"&gt; The Implementation Is Already Known   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Asynchronous parsing is not new.&lt;/p&gt; &lt;p&gt;Generators:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Exist in JavaScript for over a decade&lt;/li&gt; &lt;li&gt;Are foundational in operating systems&lt;/li&gt; &lt;li&gt;Are widely used in parsers and schedulers&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;They are exactly the right abstraction:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Incremental&lt;/li&gt; &lt;li&gt;Stateful&lt;/li&gt; &lt;li&gt;Deterministic&lt;/li&gt; &lt;li&gt;Interruptible&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Nothing exotic is required.&lt;/p&gt;  &lt;h2 id="the-critical-difference"&gt; The Critical Difference   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;The most important outcome of such a system is this:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;It can correctly deny you access.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;This places it in a completely different category than LLM-driven systems.&lt;/p&gt; &lt;p&gt;LLMs aim to be helpful.&lt;br&gt; Formal systems aim to be correct.&lt;/p&gt; &lt;p&gt;When invoking real capabilities, correctness wins.&lt;/p&gt;  &lt;h2 id="closing"&gt; Closing   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Dynamically generated languages and centralized capability graphs solve the same class of problems as MCP servers.&lt;/p&gt; &lt;p&gt;They do so:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Deterministically&lt;/li&gt; &lt;li&gt;Safely&lt;/li&gt; &lt;li&gt;Transparently&lt;/li&gt; &lt;li&gt;With better UX&lt;/li&gt; &lt;li&gt;And with lower long-term complexity&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This is not the future of “AI interfaces”.&lt;/p&gt; &lt;p&gt;This is the future of &lt;strong&gt;intent architecture&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Service as Architecture Reversal</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Mon, 12 Jan 2026 07:22:50 +0000</pubDate>
      <link>https://dev.to/djuleayo/service-as-architecture-reversal-2gli</link>
      <guid>https://dev.to/djuleayo/service-as-architecture-reversal-2gli</guid>
      <description>&lt;h1 id="service-as-architecture-reversal"&gt; Service as Architecture Reversal   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;p&gt;In the early internet, the intent was simple: distribute textual files with minimal markup.&lt;br&gt; To support this, a client–server architecture was adopted.&lt;/p&gt; &lt;p&gt;The naming was not accidental.&lt;/p&gt; &lt;p&gt;A &lt;strong&gt;server served&lt;/strong&gt;.&lt;br&gt; The &lt;strong&gt;client was the master&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;The server could not act unless requested. It could not speak unless spoken to. This asymmetry was a core design principle of the internet.&lt;/p&gt; &lt;p&gt;Today, our intuition feels inverted.&lt;/p&gt; &lt;p&gt;We scroll.&lt;br&gt; We are notified.&lt;br&gt; We are fed.&lt;/p&gt; &lt;p&gt;It feels as if we are no longer the masters of the system, but its dependents.&lt;/p&gt; &lt;p&gt;How did this reversal happen?&lt;/p&gt; &lt;h2 id="capability"&gt; Capability   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;A service exposes a capability.&lt;/p&gt; &lt;p&gt;Take a barber. A haircut is a convenience — until you cannot cut your own hair.&lt;br&gt; At that point, convenience becomes dependency.&lt;/p&gt; &lt;p&gt;The moment you lose capability, the service becomes a &lt;strong&gt;control surface&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;This is exactly what happened to software.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Software as a Service is an architectural reversal&lt;/strong&gt;:&lt;br&gt; instead of software serving on request, access to capability itself is mediated.&lt;/p&gt; &lt;p&gt;You no longer act directly.&lt;br&gt; You must go through the service.&lt;/p&gt; &lt;h2 id="control-surfaces"&gt; Control Surfaces   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Consider a simple example: taking a screenshot in WhatsApp.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You own the hardware.&lt;/li&gt; &lt;li&gt;The hardware is capable.&lt;/li&gt; &lt;li&gt;The action is legal.&lt;/li&gt; &lt;li&gt;You want to do it.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Yet you cannot.&lt;/p&gt; &lt;p&gt;The application forbids it.&lt;br&gt; And who grants the application that authority?&lt;/p&gt; &lt;p&gt;The operating system.&lt;/p&gt; &lt;p&gt;This is the root.&lt;/p&gt; &lt;p&gt;Modern operating systems increasingly allow applications to define rules over hardware you own.&lt;br&gt; Capability is no longer assumed — it is granted.&lt;/p&gt; &lt;p&gt;A second example is even more revealing: &lt;strong&gt;offline functionality&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Your computer is powerful enough to edit documents, organize files, or process data locally.&lt;br&gt; Yet many tools refuse to function without an internet connection.&lt;/p&gt; &lt;p&gt;Not because computation is impossible — but because capability has been relocated behind a service boundary.&lt;/p&gt; &lt;p&gt;When the network disappears, so does your ability to act.&lt;/p&gt; &lt;h2 id="the-reversal"&gt; The Reversal   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Most people will never modify an operating system.&lt;br&gt; They will never build tools.&lt;br&gt; They will accept the service.&lt;/p&gt; &lt;p&gt;And so the architecture completes its reversal.&lt;/p&gt; &lt;p&gt;The system that was meant to serve becomes a gatekeeper.&lt;br&gt; The user that was meant to command becomes dependent.&lt;/p&gt; &lt;p&gt;This is not a conspiracy.&lt;br&gt; It is the cumulative result of convenience traded for capability.&lt;/p&gt; &lt;h2 id="resolution"&gt; Resolution   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;The loss of freedom did not begin with surveillance or advertising.&lt;br&gt; It began earlier — with the quiet removal of user capability.&lt;/p&gt; &lt;p&gt;The way forward is not rejecting services.&lt;br&gt; It is &lt;strong&gt;refusing to confuse convenience with ownership&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Use services where they save time.&lt;br&gt; Avoid them where they become gates.&lt;/p&gt; &lt;p&gt;Prefer tools that work locally.&lt;br&gt; Prefer systems that degrade gracefully.&lt;br&gt; Prefer architectures where capability exists &lt;strong&gt;before&lt;/strong&gt; permission.&lt;/p&gt; &lt;p&gt;Because in the end, whoever holds capability holds agency.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Freedom didn’t disappear when we were watched.&lt;br&gt; It disappeared when we stopped being capable.&lt;/strong&gt;&lt;/p&gt;


&lt;/blockquote&gt;

</description>
      <category>architecture</category>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Truth of IT, AI and sense</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Sat, 10 Jan 2026 07:22:50 +0000</pubDate>
      <link>https://dev.to/djuleayo/truth-of-it-ai-and-sense-1gi9</link>
      <guid>https://dev.to/djuleayo/truth-of-it-ai-and-sense-1gi9</guid>
      <description>&lt;h1 id="truth-of-it-ai-and-sense"&gt; Truth of IT, AI and sense   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;h2 id="it-is-power"&gt; IT Is Power   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;&lt;a href="https://www.djuleayo.com/posts/power-of-automation/" rel="noopener noreferrer"&gt;Automation has been world-scale power since World War II.&lt;/a&gt; AI does not change that. It only changes who can access it.&lt;/p&gt; &lt;p&gt;Automation has never worked “for you” by default. AI will not either. The same defensive mechanisms apply.&lt;/p&gt; &lt;p&gt;The system protects itself.&lt;/p&gt; &lt;p&gt;Fragmentation is one of those defenses. It prevents uninvited rises to power.&lt;/p&gt; &lt;p&gt;If you want market share, you are told you need an app on four platforms. To build that, you already need capital, distribution, and legitimacy. If you want traction, you need money. To get money, you need traction.&lt;/p&gt; &lt;p&gt;This is not accidental. It is the essence of pyramidal systems.&lt;/p&gt; &lt;p&gt;In any direct competition, the decisive advantage is surprise — a weapon the opponent does not expect. Markets are no different. Whether drugs, services, or software, the rule is the same: you want customers to know you exist while competitors do not.&lt;/p&gt; &lt;p&gt;Yet newcomers are taught the opposite: build in public, share everything, come with open hands — as if power has ever been granted freely.&lt;/p&gt; &lt;p&gt;In cultures with inherited capital and institutional trust, this illusion can survive. Elsewhere, it functions as extraction. The promise is always the same: someone won the lottery — work hard and it might be you.&lt;/p&gt; &lt;p&gt;Cultural memory has warned about this for millennia. Prometheus was not punished for kindness, but for stealing power prematurely.&lt;/p&gt; &lt;p&gt;Smokescreens are not moral failures. They are safeguards.&lt;/p&gt; &lt;p&gt;Those who see them can navigate. Those who do not are filtered out.&lt;/p&gt; &lt;h2 id="the-smokescreen"&gt; The Smokescreen   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;This is written from nearly two decades behind a screen.&lt;/p&gt; &lt;p&gt;For two years I was paid well to build what was, in essence, a button calling an external service. That is not a complaint. It provided time and money. Both matter.&lt;/p&gt; &lt;p&gt;But once you build something real, you notice how aggressively usefulness is devalued.&lt;/p&gt; &lt;p&gt;Liquidity floods products whose primary function is compliance: don’t shake the boat — you have a job.&lt;/p&gt; &lt;p&gt;Todo apps raised billions. Not because they automate meaningfully, but because they are safe.&lt;/p&gt; &lt;p&gt;Most “creation” today is binding: gluing together logging services, monitoring services, analytics services, translation services, storage services, auth services, payment services.&lt;/p&gt; &lt;p&gt;What did you build?&lt;/p&gt; &lt;p&gt;This is not laziness. It is guided behavior.&lt;/p&gt; &lt;p&gt;Money flows steer developers away from capability and toward dependency. The web itself is structured around this principle.&lt;/p&gt; &lt;p&gt;Now widen the lens.&lt;/p&gt; &lt;p&gt;How many people in a country are builders by character? How many are trained as engineers? How many end up employed by foreign companies assembling SaaS stacks, mistaking API calls for ownership, platforms for infrastructure?&lt;/p&gt; &lt;p&gt;They are not idle. They are productive — inside fragmentation.&lt;/p&gt; &lt;p&gt;The result is predictable: local capability erodes, and sovereignty shifts to external platforms delivered “as a service.”&lt;/p&gt; &lt;p&gt;Even technical people lose power.&lt;/p&gt; &lt;p&gt;&lt;a href="https://www.djuleayo.com/posts/uxmetrix/" rel="noopener noreferrer"&gt;UX that is not o(1) is not automation.&lt;/a&gt; It is control through friction.&lt;/p&gt; &lt;h2 id="ai-power-redistribution"&gt; AI Power Redistribution   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;AI changes one thing decisively:&lt;/p&gt; &lt;p&gt;Execution is no longer the bottleneck.&lt;/p&gt; &lt;p&gt;Coding agents, code search, and automated infrastructure inheritance remove manpower scarcity. Workforce becomes cheap. Speed becomes abundant.&lt;/p&gt; &lt;p&gt;Capability does not.&lt;/p&gt; &lt;p&gt;Scaling still depends on abstraction. Abstractions that can absorb exceptions without collapsing. Abstractions coherent enough to survive growth.&lt;/p&gt; &lt;p&gt;To the unskilled eye, high-level abstraction looks like noise. Rubbish and abstraction are indistinguishable without judgment.&lt;/p&gt; &lt;p&gt;That is the final bottleneck.&lt;/p&gt; &lt;p&gt;As execution flattens, control passes to those who own abstractions — not organizations with headcount. Companies will depend on them, not the reverse. Not out of ideology, but out of necessity.&lt;/p&gt; &lt;p&gt;This is not something that “must” stay so. It stays so because scaling fails without it.&lt;/p&gt; &lt;p&gt;For the first time, mechanics are no longer the constraint. Capability is.&lt;/p&gt; &lt;p&gt;Fragmentation declines. Coherent capability compounds.&lt;/p&gt; &lt;p&gt;These tools being public is not generosity. It is admission.&lt;/p&gt; &lt;p&gt;Those who can use them are rare. Those who cannot will be displaced — regardless of effort.&lt;/p&gt; &lt;p&gt;For a builder, this shift is not motivational. It is structural.&lt;/p&gt; &lt;h2 id="selection"&gt; Selection   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;With power comes responsibility — but responsibility is not assigned by declaration.&lt;/p&gt; &lt;p&gt;The remaining work is not invention. It is inheritance.&lt;/p&gt; &lt;p&gt;Understanding existing intent. Indexing accumulated systems. Maintaining coherence under growth.&lt;/p&gt; &lt;p&gt;Very few can do this. Fewer will choose to.&lt;/p&gt; &lt;p&gt;No invitation is required. The system selects on its own.&lt;/p&gt; &lt;p&gt;Those who align will find leverage. Those who do not will fragment — efficiently, at scale.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>discuss</category>
    </item>
    <item>
      <title>You Should Not Outsource Your Topological Sort</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Sat, 20 Dec 2025 09:13:48 +0000</pubDate>
      <link>https://dev.to/djuleayo/you-should-not-outsource-your-topological-sort-311d</link>
      <guid>https://dev.to/djuleayo/you-should-not-outsource-your-topological-sort-311d</guid>
      <description>&lt;h1 id="you-should-not-outsource-your-topological-sort"&gt; You Should Not Outsource Your Topological Sort   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;p&gt;Things are shifting quickly in software.&lt;/p&gt; &lt;p&gt;Execution is no longer scarce. Parallelism is cheap. Spawning workers—human or AI—is trivial. What is no longer cheap is &lt;strong&gt;coherence&lt;/strong&gt;: understanding what depends on what, and in which order work can safely progress.&lt;/p&gt; &lt;p&gt;In that world, seniority is not about knowing frameworks or tooling. It is about &lt;strong&gt;owning structure&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;One structure, in particular, keeps reappearing.&lt;/p&gt;  &lt;h2 id="the-invariant-we-keep-outsourcing"&gt; The invariant we keep outsourcing   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Topological sort sounds boring. Dependency graphs. Build order. Module resolution.&lt;/p&gt; &lt;p&gt;For years, we happily outsourced this problem:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;to module loaders&lt;/li&gt; &lt;li&gt;to dependency injectors&lt;/li&gt; &lt;li&gt;to build systems&lt;/li&gt; &lt;li&gt;to frameworks&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;And that was fine—when execution itself was the bottleneck.&lt;/p&gt; &lt;p&gt;But once you start orchestrating:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;multiple worktrees&lt;/li&gt; &lt;li&gt;multiple agents&lt;/li&gt; &lt;li&gt;partially overlapping tasks&lt;/li&gt; &lt;li&gt;incremental pipelines&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;…the dependency graph stops being an implementation detail.&lt;/p&gt; &lt;p&gt;It becomes &lt;strong&gt;the control surface&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="what-breaks-when-you-dont-own-it"&gt; What breaks when you don’t own it   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;If the dependency graph lives outside your system, you lose more than convenience:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You can’t checkpoint progress meaningfully&lt;/li&gt; &lt;li&gt;You can’t resume work without recomputation&lt;/li&gt; &lt;li&gt;You can’t branch execution deterministically&lt;/li&gt; &lt;li&gt;You can’t define clean contract boundaries between concurrent actors&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;You’re forced to “start over” more often than necessary—not because the work changed, but because &lt;strong&gt;you don’t know where you are&lt;/strong&gt; in the graph.&lt;/p&gt; &lt;p&gt;This shows up everywhere:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;painful monorepo setups&lt;/li&gt; &lt;li&gt;slow feedback loops&lt;/li&gt; &lt;li&gt;brittle automation&lt;/li&gt; &lt;li&gt;orchestration that feels magical instead of mechanical&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;These are not tooling problems. They are &lt;strong&gt;graph ownership problems&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="a-concrete-example-sessions-as-graph-cursors"&gt; A concrete example: sessions as graph cursors   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Imagine a single source of truth: code, data, or input.&lt;/p&gt; &lt;p&gt;You process it in stages:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;tokenization&lt;/li&gt; &lt;li&gt;preprocessing&lt;/li&gt; &lt;li&gt;parsing&lt;/li&gt; &lt;li&gt;semantic analysis&lt;/li&gt; &lt;li&gt;domain-specific passes&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Different consumers need different depths.&lt;br&gt; But you do &lt;em&gt;not&lt;/em&gt; want to reprocess from scratch every time.&lt;/p&gt; &lt;p&gt;What you actually want is simple:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;recognize how far the source has been processed&lt;/li&gt; &lt;li&gt;continue minimally from there&lt;/li&gt; &lt;li&gt;reuse everything that is already valid&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;That is not a caching trick.&lt;/p&gt; &lt;p&gt;That is a &lt;strong&gt;cursor moving through a DAG&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Call it a session. Call it an account. The name doesn’t matter.&lt;/p&gt; &lt;p&gt;What matters is this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;once you own the dependency graph, incremental progress becomes trivial.&lt;/p&gt;


&lt;/blockquote&gt; &lt;p&gt;Without it, you fake it.&lt;/p&gt;  &lt;h2 id="why-this-matters-more-now"&gt; Why this matters more now   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;With AI agents, automation, and parallel execution:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;execution is abundant&lt;/li&gt; &lt;li&gt;retries are cheap&lt;/li&gt; &lt;li&gt;recomputation is wasteful&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The limiting factor is no longer &lt;em&gt;doing work&lt;/em&gt;.&lt;br&gt; It is &lt;strong&gt;placing work correctly&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;If you outsource topological reasoning, you:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;lose observability&lt;/li&gt; &lt;li&gt;lose control&lt;/li&gt; &lt;li&gt;lose leverage&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;And no amount of tooling on top can recover that.&lt;/p&gt;  &lt;h2 id="the-real-claim"&gt; The real claim   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;This is not about implementing topological sort yourself for sport.&lt;/p&gt; &lt;p&gt;It is about this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;You should not outsource the authority over your dependency graph.&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;/blockquote&gt; &lt;p&gt;Once you own it:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;session management becomes natural&lt;/li&gt; &lt;li&gt;incremental computation falls out&lt;/li&gt; &lt;li&gt;agent contracts become explicit&lt;/li&gt; &lt;li&gt;orchestration becomes deterministic instead of heuristic&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Once you don’t:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;everything becomes “best effort”&lt;/li&gt; &lt;li&gt;progress becomes opaque&lt;/li&gt; &lt;li&gt;automation becomes fragile&lt;/li&gt; &lt;/ul&gt;  &lt;h2 id="closing"&gt; Closing   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Topological sort is not an algorithm problem anymore.&lt;/p&gt; &lt;p&gt;It is a &lt;strong&gt;design boundary&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Outsource it, and you outsource understanding.&lt;br&gt; Own it, and higher-level abstractions finally become possible.&lt;/p&gt; &lt;p&gt;That choice matters more now than it ever did.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>architecture</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>TS API Spec</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Sat, 20 Dec 2025 09:13:48 +0000</pubDate>
      <link>https://dev.to/djuleayo/ts-api-spec-2n49</link>
      <guid>https://dev.to/djuleayo/ts-api-spec-2n49</guid>
      <description>&lt;h1 id="ts-api-spec"&gt; TS API Spec   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;ul&gt; &lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@apispects/core" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.npmjs.com/package/@apispects/core" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@apispects/core&lt;/a&gt;
&lt;/li&gt; &lt;li&gt;repo: &lt;a href="https://github.com/apispects/core" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/apispects/core" rel="noopener noreferrer"&gt;https://github.com/apispects/core&lt;/a&gt;
&lt;/li&gt; &lt;/ul&gt;  &lt;h2 id="ts-first-api-spec-with-superb-dx"&gt; TS-first API spec with superb DX   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;img src="/tsapispec.demo.gif"&gt;  &lt;p&gt;Everything is autocompleted and type-checked.&lt;br&gt; As soon as the schema is satisfied, the error disappears.&lt;/p&gt; &lt;p&gt;No separate spec file.&lt;br&gt; No generation step.&lt;/p&gt;  &lt;h2 id="the-problem-this-targets"&gt; The problem this targets   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Most teams still pay the same tax:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;duplicated contracts (controllers, OpenAPI, generated clients)&lt;/li&gt; &lt;li&gt;contract drift between backend and frontend&lt;/li&gt; &lt;li&gt;stale generated code&lt;/li&gt; &lt;li&gt;runtime bugs shipped by “green” builds&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The root cause is structural:&lt;br&gt; &lt;strong&gt;the contract is treated as an artifact, not as the boundary.&lt;/strong&gt;&lt;/p&gt;  &lt;h2 id="benefits"&gt; Benefits   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;ul&gt; &lt;li&gt;
&lt;strong&gt;Maximum code sharing between nodes&lt;/strong&gt; (assuming JS/TS)&lt;/li&gt; &lt;li&gt;
&lt;strong&gt;Fully typed end-to-end&lt;/strong&gt;, including error cases&lt;/li&gt; &lt;li&gt;
&lt;strong&gt;Uniform error handling&lt;/strong&gt; across server and client&lt;/li&gt; &lt;li&gt;
&lt;strong&gt;Single source of truth&lt;/strong&gt;: router and apiClient are generated from the same spec&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Optional runtime validation can be enabled in non-prod without changing the contract.&lt;/p&gt;  &lt;h2 id="overlap-with-existing-approaches"&gt; Overlap with existing approaches   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;This overlaps with:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;OpenAPI + codegen&lt;/li&gt; &lt;li&gt;RPC frameworks&lt;/li&gt; &lt;li&gt;shared schema repos&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Difference:&lt;/strong&gt; those generate code &lt;em&gt;from&lt;/em&gt; a spec.&lt;br&gt; Here, the spec &lt;em&gt;is&lt;/em&gt; the code boundary.&lt;/p&gt;  &lt;h2 id="why-this-is-especially-leveraged-in-jsts"&gt; Why this is especially leveraged in JS/TS   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;JS has an unfair advantage:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;same language on backend and frontend&lt;/li&gt; &lt;li&gt;TypeScript as the shared type system&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;That enables:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;zero drift&lt;/li&gt; &lt;li&gt;zero regeneration&lt;/li&gt; &lt;li&gt;no stale specs&lt;/li&gt; &lt;/ul&gt;  &lt;h2 id="example"&gt; Example   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Declare your spec once:&lt;/p&gt; &lt;div class="highlight js-code-highlight"&gt;
&lt;pre&gt;&lt;code data-lang="ts"&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; authRouter &lt;span&gt;=&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; register_post&lt;span&gt;:&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; bodySchema: &lt;span&gt;registerRequestSchema&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; responseSchema: &lt;span&gt;z.object&lt;/span&gt;({ token: &lt;span&gt;z.string&lt;/span&gt;() }), &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; cbErrorSchema: &lt;span&gt;registrationError&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; login_post&lt;span&gt;:&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; bodySchema: &lt;span&gt;loginRequestSchema&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; responseSchema: &lt;span&gt;z.object&lt;/span&gt;({ token: &lt;span&gt;z.string&lt;/span&gt;() }), &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; cbErrorSchema: &lt;span&gt;loginErrors&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; refreshToken_post&lt;span&gt;:&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; headerSchema: &lt;span&gt;authHeader&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; cbErrorSchema: &lt;span&gt;z.null&lt;/span&gt;(), &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; responseSchema: &lt;span&gt;z.object&lt;/span&gt;({ token: &lt;span&gt;z.string&lt;/span&gt;() }), &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; } &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;} &lt;span&gt;as&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; satisfies ApiSpec; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; apiSpec &lt;span&gt;=&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; api&lt;span&gt;:&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; auth: &lt;span&gt;authRouter&lt;/span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; macro: &lt;span&gt;macroRouter&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; } &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;} &lt;span&gt;as&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; satisfies ApiSpec; &lt;span&gt;// key line (TS 5) &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;//Generate both router and client from the same spec: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt; { router: &lt;span&gt;apiRouter&lt;/span&gt; } &lt;span&gt;=&lt;/span&gt; makeApi(apiSpec, {}, express.Router); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;//Controller implementation is fully typed by construction: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;makeController( &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; authRouter.register_post, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;async&lt;/span&gt; ({ body&lt;span&gt;:&lt;/span&gt; { email, name, password } }) &lt;span&gt;=&amp;gt;&lt;/span&gt; { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;const&lt;/span&gt; user &lt;span&gt;=&lt;/span&gt; &lt;span&gt;await&lt;/span&gt; registerUser(email, name, password); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;if&lt;/span&gt; (&lt;span&gt;typeof&lt;/span&gt; user &lt;span&gt;===&lt;/span&gt; &lt;span&gt;'string'&lt;/span&gt;) &lt;span&gt;return&lt;/span&gt; user; &lt;span&gt;// typed error case &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;return&lt;/span&gt; { token: &lt;span&gt;jwtSign&lt;/span&gt;({ userId: &lt;span&gt;user.id&lt;/span&gt; }) }; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; } &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;//And the client derives from the same contract: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt; &lt;span&gt;const&lt;/span&gt; { apiClient } &lt;span&gt;=&lt;/span&gt; makeApi(apiSpec, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; baseUrl&lt;span&gt;:&lt;/span&gt; &lt;span&gt;'&lt;a href="http://localhost:3033'" rel="noopener noreferrer"&gt;http://localhost:3033&amp;amp;#39&lt;/a&gt;;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;}); &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;If you violate the contract on either side, TypeScript complains immediately.&lt;/p&gt;  &lt;h2 id="how-its-built"&gt; How it’s built   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;ul&gt; &lt;li&gt;
&lt;strong&gt;Parser types&lt;/strong&gt; (the core mechanism): &lt;a href="http://djuleayo.com/posts/parser-types/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="http://djuleayo.com/posts/parser-types/" rel="noopener noreferrer"&gt;http://djuleayo.com/posts/parser-types/&lt;/a&gt;
&lt;/li&gt; &lt;li&gt;
&lt;strong&gt;TypeScript 5 + Vite&lt;/strong&gt; for clean cross-module sharing&lt;/li&gt; &lt;/ul&gt;  &lt;h2 id="what-this-optimizes-for"&gt; What this optimizes for   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;ul&gt; &lt;li&gt;correctness over ceremony&lt;/li&gt; &lt;li&gt;DX over documentation&lt;/li&gt; &lt;li&gt;contracts that cannot drift by construction&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;If you can break the contract without TypeScript complaining, the setup is wrong.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>api</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Recursive "parser/grammar" type TS metaprogramming</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Fri, 25 Jul 2025 08:23:31 +0000</pubDate>
      <link>https://dev.to/djuleayo/recursive-parsergrammar-type-ts-metaprogramming-1n4l</link>
      <guid>https://dev.to/djuleayo/recursive-parsergrammar-type-ts-metaprogramming-1n4l</guid>
      <description>&lt;p&gt;Original post: &lt;a href="http://www.djuleayo.com/posts/parser-types/" rel="noopener noreferrer"&gt;http://www.djuleayo.com/posts/parser-types/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro — formal languages and parsing
&lt;/h2&gt;

&lt;p&gt;All programming languages are specified by a grammar. A grammar tells us whether a piece of text is a valid sentence in that language. A parser generator can take such a grammar and produce a parser. Parsing is the “front end” of compilation: it turns raw text into structure. That’s the basis of how formal languages work.&lt;/p&gt;

&lt;p&gt;Simplistic grammar example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%token &amp;lt;int&amp;gt; NUMBER

expression: expression '+' expression
          | expression '-' expression
          | expression '*' expression
          | expression '/' expression
          | '(' expression ')'
          | NUMBER
          ;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This grammar can generate a parser for simple arithmetic. Before parsing, the input is just text. After parsing, we both &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(a) know whether it’s valid under this grammar and &lt;/li&gt;
&lt;li&gt;(b) obtain a structured representation of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 + 2 * (3 - 4)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parsed tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        +
       / \
      1   *
         / \
        2   -
           / \
          3   4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Operator precedence and associativity are encoded in how the tree “leans.” This is an Abstract Syntax Tree (AST). Every program you’ve ever written is, at some level, an AST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative vs. imperative (why we like trees-as-data)
&lt;/h2&gt;

&lt;p&gt;An AST is a tree, and a tree is data. Code can be seen as data laid out in a shape that captures meaning. The tree is a constant shape that other parts of the system can consume. That’s powerful.&lt;/p&gt;

&lt;p&gt;Take an Express router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’re not imperatively pushing bytes through sockets; you declare what should happen on /users. The framework consumes that declaration at the right time.&lt;/p&gt;

&lt;p&gt;Declarative style usually means: you state what you want; the machinery figures out how. The more capable the machinery, the richer the configuration language must be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parser types
&lt;/h2&gt;

&lt;p&gt;Configurations aren’t always free-form; values often depend on each other. Not every config that “type-checks” at the top level is semantically valid. Think of a configuration as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a constant value that must satisfy a language (expressed as an AST)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If that clicks, good. TypeScript lets us enforce such languages at compile time. I call these parser types: recursive TS types that act like a grammar. They don’t parse tokens; they constrain shape the way a grammar constrains sentences.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ can't do&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TS sees a naked, non-terminating recursion and bails.&lt;/p&gt;

&lt;p&gt;Even:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;still trips the checker.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="c1"&gt;// ✅ can do&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;works. The index signature form delays evaluation enough for TS to accept the recursion. That’s our recursive hook. Depth is unbounded (until compiler limits), so it can “eat” arbitrarily deep objects. The remaining task is to encode your language rules with unions and recursion.&lt;/p&gt;

&lt;p&gt;TypeScript even nudges us syntactically—the leading pipes look exactly like grammar alternatives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Literal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your arithmetic example as a type-level grammar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Literal_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// 1) leaf&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NumberLiteral&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2) binary&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BinaryExpr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 3) grouping&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ParenExpr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 4) the one non-recursive alias&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NumberLiteral&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;BinaryExpr&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ParenExpr&lt;/span&gt;
  &lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// type annotation assures the object matches the grammar as AST&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ❌ type error&lt;/span&gt;
    &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That stray test: 'asdf' is illegal under the grammar and TypeScript tells you immediately. Bonus: editor autocomplete now “knows” exactly what belongs where in your config/AST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would you care?
&lt;/h2&gt;

&lt;p&gt;Already mentioned: grammar can check more then any type union can.&lt;br&gt;
But another area is COMPOSITE PATTERN constants. IE JSX is composite.&lt;br&gt;
Child of a node is again JSX node. Composite patterns are omnipresent yet &lt;br&gt;
constants of composite patterns are very error prone. Especially written by hand&lt;br&gt;
without assistance of language server. Now JSX is solved as it is. Syntax sugar&lt;br&gt;
for function calls. Thats runtime in essence and not a constant.&lt;/p&gt;

&lt;p&gt;I'll hint another example of composite that with some effort of defining &lt;br&gt;
proper "parser type", we can declare as constants with full type support.&lt;br&gt;
Server routers. They are composable. and leafs are controllers.&lt;br&gt;
Such constant would be SSOT which is effectively shared contract between server and client.&lt;br&gt;
Out of the box both api client and controller functions would have full type support.&lt;br&gt;
You can say, well we can use open API spec for that. But such a type would&lt;br&gt;
allow full type support just out of declaration and nothing more. No generate types&lt;br&gt;
step, code gen, eventual DRY hell retyping schemas and so on. &lt;br&gt;
In next post I'll show how to do that with a real example. Stay tuned.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>FP vs IP and Microservices vs Monoliths are the same argument</title>
      <dc:creator>djuleayo</dc:creator>
      <pubDate>Thu, 15 May 2025 15:10:43 +0000</pubDate>
      <link>https://dev.to/djuleayo/fp-vs-ip-and-microservices-vs-monoliths-are-the-same-argument-1mc5</link>
      <guid>https://dev.to/djuleayo/fp-vs-ip-and-microservices-vs-monoliths-are-the-same-argument-1mc5</guid>
      <description>&lt;h1 id="fp-vs-ip-and-microservices-vs-monoliths-are-the-same-argument"&gt; FP vs IP and Microservices vs Monoliths are the same argument   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h1&gt; &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y4fiyopvtavaah0bbgo.png" width="800" height="1200"&gt;&lt;p&gt;There is a huge parallel between:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;functional vs imperative programming&lt;/li&gt; &lt;li&gt;microservices vs monoliths&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Now, the most unwelcome thing—dogma—should have no foothold in the software world. But it does.&lt;br&gt; Just like other markets, we ride waves of virality. It’s no coincidence that most beginners choose the most popular frameworks and languages.&lt;/p&gt; &lt;p&gt;While this kind of marketing gives guidance, it destroys the very essence of what coding should be: &lt;strong&gt;sense&lt;/strong&gt;.&lt;br&gt; So parrots fly around and repeat things like:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;“Microservices are better than monoliths”&lt;/li&gt; &lt;li&gt;“Functional programming is better than imperative programming”&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;But both approaches live, both are used—and that alone tells us both are good and both have their place.&lt;/p&gt; &lt;p&gt;Let’s draw the parallel between these two debates and expose the hype for what it is.&lt;/p&gt;  &lt;h2 id="the-microservice-hype"&gt; The Microservice Hype   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Every aspiring developer is told they must know how to build scalable systems before they can land a job—as if each of us is building cloud-scale infrastructure for side projects.&lt;/p&gt; &lt;p&gt;Buzzwords in job postings feed the hype, and so do slogans like:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;“Your microservice team can be fed by one pizza.”&lt;/li&gt; &lt;li&gt;“Keep your services stateless.”&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;How many times have I heard the second one in interviews—only to open the source code and find a constructor call inside a &lt;code&gt;get&lt;/code&gt; method, of a class passed via dependency injection… in JavaScript.&lt;/p&gt; &lt;p&gt;Come on. Time to quit (this really happened 😅).&lt;/p&gt;  &lt;h2 id="java-overengineering-in-javascript"&gt; Java Overengineering in JavaScript   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Java-style overengineering doesn’t belong in JavaScript.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Your JS module is automatically a singleton (unless you use dynamic imports).&lt;/li&gt; &lt;li&gt;The runtime handles circular dependency resolution.&lt;/li&gt; &lt;li&gt;Your module’s global-scope variables can’t be mutated from the outside without you &lt;strong&gt;exposing&lt;/strong&gt; an interface to do so.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;And yet, people bring Java complexity patterns into JS like it’s a rite of passage.&lt;/p&gt;  &lt;h2 id="bitter-sweet-reality"&gt; Bitter sweet reality   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;This brings us to the real point: most devs are beginners. This will always be true. Most of us are territorial (we defend our code even when it sucks), and many are driven by the desire to be &lt;strong&gt;right&lt;/strong&gt;—or at least to appear smart.&lt;/p&gt; &lt;p&gt;This, combined with a general lack of experience, makes it almost a rule: &lt;strong&gt;large codebases will be colored with smells&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Even “stateless” microservices often use stateful libraries or sessions for auth. Perspective is lacking—and that lack is guarded by dogma, which ultimately reduces to social behavior.&lt;/p&gt; &lt;p&gt;And maybe that’s fine.&lt;br&gt; After all, every problem should’ve been solved yesterday. Business doesn’t wait. We must deliver. Fast.&lt;/p&gt; &lt;p&gt;So there’s no time for:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;“If you want to make pasta from scratch, you must first invent the universe.”&lt;/em&gt;&lt;br&gt; — Carl Sagan&lt;/p&gt;


&lt;/blockquote&gt;  &lt;h2 id="services-should-be-focused"&gt; Services Should Be Focused   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Like modules, services should focus on &lt;strong&gt;one thing&lt;/strong&gt; and do it well.&lt;/p&gt; &lt;p&gt;Why? So we can scale them horizontally, reuse them, and maintain them independently.&lt;/p&gt; &lt;p&gt;But here’s where inexperience hits.&lt;br&gt; Many devs—even seniors in web—have never written a proper multithreaded program. Those who have know that &lt;strong&gt;orchestrating multiple clones of the same thing requires overhead&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;In microservices, this overhead is primarily the &lt;strong&gt;cost of statelessness&lt;/strong&gt;.&lt;/p&gt;  &lt;h2 id="stateless-by-design--and-by-cost"&gt; Stateless by Design — and by Cost   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Your message queues, Redis instances, and storage layers all need to be stateless (or used in a stateless way), because &lt;strong&gt;synchronizing across clones&lt;/strong&gt; goes against horizontal scaling.&lt;/p&gt; &lt;p&gt;Need more performance? Spin up more instances.&lt;/p&gt; &lt;p&gt;The more instances you have, the harder it gets to synchronize them.&lt;br&gt; So… just don’t.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Don’t cache.&lt;/li&gt; &lt;li&gt;Don’t store session data.&lt;/li&gt; &lt;li&gt;Don’t share memory.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Instead: &lt;strong&gt;recompute everything&lt;/strong&gt; every time.&lt;/p&gt; &lt;p&gt;Just like how a server-side page is re-rendered per request.&lt;/p&gt;  &lt;h2 id="example"&gt; Example   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Let’s put it in beginner-friendly terms.&lt;/p&gt; &lt;p&gt;Say you have a function:&lt;/p&gt; &lt;div class="highlight js-code-highlight"&gt;
&lt;pre&gt;&lt;code data-lang="python"&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;fibonacci&lt;/span&gt;(n): &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now you call:&lt;/p&gt; &lt;div class="highlight js-code-highlight"&gt;
&lt;pre&gt;&lt;code data-lang="python"&gt;&lt;span&gt;&lt;span&gt;print(fibonacci(&lt;span&gt;100&lt;/span&gt;)) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;print(fibonacci(&lt;span&gt;101&lt;/span&gt;)) &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Neither functional programming nor microservices care that you just computed fibonacci(100). On line two, you start from scratch.&lt;/p&gt; &lt;p&gt;Only difference? A microservice wraps it in an HTTP request.&lt;/p&gt; &lt;p&gt;So the real question becomes:&lt;/p&gt; &lt;p&gt;Why is this better than a monolith? Why is it better than imperative code that could just cache the result—scaling vertically and holding runtime state with ease?&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;“Devs can think much further than they can code. Power to express oneself is lacking.”&lt;/p&gt;
&lt;br&gt;
&lt;/blockquote&gt; &lt;p&gt;This is the key.&lt;/p&gt; &lt;p&gt;Recomputing is obviously more expensive than caching, from the machine’s perspective. But from the developer’s perspective?&lt;/p&gt; &lt;p&gt;Caching is low-level. Tedious. Demanding. Often lacking expressive power. So we avoid it.&lt;/p&gt; &lt;p&gt;Instead, we build massive CI pipelines, spin nuclear-scale infrastructure, and recompute everything since Christ—just to avoid managing memory.&lt;/p&gt; &lt;h2 id="scaling-with-clones-vs-standing-on-state"&gt; Scaling with Clones vs Standing on State   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;Functional programming and microservices recompute anew—but can easily spawn 1000 instances of the same thing.&lt;/p&gt; &lt;p&gt;Imperative programming and monoliths tend to revolve around a single instance—but with the benefit of retained state and optimized memory usage.&lt;/p&gt; &lt;p&gt;Which one is better?&lt;/p&gt; &lt;p&gt;No straight answer.&lt;/p&gt; &lt;h2 id="protecting-the-codebase-from-your-own-team"&gt; Protecting the Codebase from Your Own Team   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;If you have a large team, you often want to restrict expressive power.&lt;/p&gt; &lt;p&gt;You narrow responsibilities so each dev works in isolation. This reduces risk, reduces damage, and speeds up onboarding.&lt;/p&gt; &lt;p&gt;Yes, expressive power is linked to production cost and complexity. And industry cares more about delivering features than squeezing performance.&lt;/p&gt; &lt;p&gt;Under one condition:&lt;/p&gt; &lt;h2 id="platforms-vs-applications"&gt; Platforms vs Applications   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;We can divide software into two rough categories:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Infrastructure — platforms like compilers, browsers, frameworks, and libraries&lt;/li&gt; &lt;li&gt;Applications — business logic and user-facing systems&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;If you fall into the second category, you tend to value:&lt;/p&gt; &lt;p&gt;Expressive power&lt;/p&gt; &lt;p&gt;Fast iteration&lt;/p&gt; &lt;p&gt;High-level abstraction&lt;/p&gt; &lt;p&gt;That’s when you lean on frameworks, CI pipelines, and horizontal scaling.&lt;/p&gt; &lt;p&gt;But if you’re building infrastructure, you care more about:&lt;/p&gt; &lt;p&gt;Precision&lt;/p&gt; &lt;p&gt;Performance&lt;/p&gt; &lt;p&gt;Long-term stability&lt;/p&gt; &lt;p&gt;So you optimize. You profile. You write tight, efficient code.&lt;/p&gt; &lt;h2 id="the-pendulum-swings"&gt; The Pendulum Swings   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;We can’t always compute exactly when it’s time to switch paradigms. When is it worth optimizing that slow service? When is it better to lean into scale?&lt;/p&gt; &lt;p&gt;The pendulum will keep swinging—from monolith to microservice, from FP to imperative.&lt;/p&gt; &lt;p&gt;But the ideal gap between what we can think and what we can express in code is still wide. Maybe unbridgeable.&lt;/p&gt; &lt;h2 id="the-real-limitation-human-abstraction"&gt; The Real Limitation: Human Abstraction   &lt;span&gt;Link to heading&lt;/span&gt;  &lt;/h2&gt; &lt;p&gt;It’s not that we lack tools to build expressive languages. They exist. The real problem is:&lt;/p&gt; &lt;p&gt;Human limitations in abstraction.&lt;/p&gt; &lt;p&gt;As one of my respected colleagues once said:&lt;/p&gt; &lt;p&gt;“The more abstract you go, the more language resembles static noise.”&lt;/p&gt; &lt;p&gt;And that is something worth pondering.&lt;/p&gt; &lt;p&gt;It may be the real cause behind these never-ending clashes in modern software: FP vs IP, MS vs monolith — and the limits of the human mind behind the keyboard.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
