<?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: Chris M. Pérez</title>
    <description>The latest articles on DEV Community by Chris M. Pérez (@chrismichaelps).</description>
    <link>https://dev.to/chrismichaelps</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2890018%2F111e9996-f0c0-405d-821a-d1fd17c5d891.jpg</url>
      <title>DEV Community: Chris M. Pérez</title>
      <link>https://dev.to/chrismichaelps</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chrismichaelps"/>
    <language>en</language>
    <item>
      <title>Pudu Programming Language</title>
      <dc:creator>Chris M. Pérez</dc:creator>
      <pubDate>Sat, 19 Sep 2026 02:12:42 +0000</pubDate>
      <link>https://dev.to/chrismichaelps/pudu-programming-language-42h5</link>
      <guid>https://dev.to/chrismichaelps/pudu-programming-language-42h5</guid>
      <description>&lt;p&gt;Pudu is a statically typed, expression-oriented programming language that I’m designing for building services, developer tools, and native applications. The project is still young and is currently moving toward its first &lt;code&gt;0.1.0&lt;/code&gt; pre-release, but it has grown well beyond the stage of being a syntax experiment. Today, the repository contains the language implementation, standard library, testing infrastructure, development tools, editor integration, documentation, examples, and the foundations of the ecosystem I want to build around it.&lt;/p&gt;

&lt;p&gt;I think this is the right point to start talking more openly about why Pudu exists, what I’m trying to accomplish with it, and where I want to take the language.&lt;/p&gt;

&lt;p&gt;The source code is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/chrismichaelps/pudu-lang" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/pudu-lang&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Pudu?
&lt;/h2&gt;

&lt;p&gt;Creating another programming language in 2026 naturally raises a question: why?&lt;/p&gt;

&lt;p&gt;We already have an extraordinary number of good languages. Rust has pushed systems programming toward stronger safety guarantees. Haskell has demonstrated how far expressive type systems and functional programming can go. OCaml has spent decades showing how practical and elegant the ML family can be. Go demonstrated the value of simplicity and excellent tooling. TypeScript transformed the experience of working with large JavaScript codebases.&lt;/p&gt;

&lt;p&gt;Pudu does not exist because I believe those languages have failed.&lt;/p&gt;

&lt;p&gt;It exists because programming languages are collections of trade-offs, and I wanted to explore a particular combination of them.&lt;/p&gt;

&lt;p&gt;One of the ideas behind Pudu is that important uncertainty in a program should be visible. If an operation can fail, that possibility should be represented by its type. If a value can be absent, that absence should be explicit. If a program branches over every possible form of a type, the compiler should be able to tell us when one of those possibilities has been forgotten.&lt;/p&gt;

&lt;p&gt;At the same time, I don't want those guarantees to require writing code that feels unnecessarily academic or complicated.&lt;/p&gt;

&lt;p&gt;That balance—expressiveness, explicitness, safety, and practical usability—is one of the central design problems I’m exploring with Pudu.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Pudu looks like
&lt;/h2&gt;

&lt;p&gt;A small Pudu program currently looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;Shapes&lt;/span&gt;

&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Std&lt;/span&gt;&lt;span class="py"&gt;.Io&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Io&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Float&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;
    &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;[(),&lt;/span&gt; &lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Io&lt;/span&gt;&lt;span class="nf"&gt;.writeLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;Ok&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;There are several parts of Pudu's philosophy hidden inside this relatively small example.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Shape&lt;/code&gt; is not represented through an arbitrary integer, string, or inheritance hierarchy. It is a type with a finite set of possible forms. A &lt;code&gt;Shape&lt;/code&gt; is either a &lt;code&gt;Circle&lt;/code&gt; containing a radius or a &lt;code&gt;Rectangle&lt;/code&gt; containing a width and height.&lt;/p&gt;

&lt;p&gt;When the value is inspected with &lt;code&gt;match&lt;/code&gt;, those possibilities become explicit.&lt;/p&gt;

&lt;p&gt;More importantly, matching is exhaustive. If the definition of &lt;code&gt;Shape&lt;/code&gt; evolves and another case is introduced, code matching against that type should not quietly continue while forgetting that the new state exists.&lt;/p&gt;

&lt;p&gt;I like this property because it changes the relationship between the programmer and the compiler. The compiler is not simply translating syntax into something executable. It is participating in the process of maintaining the assumptions encoded in the program.&lt;/p&gt;

&lt;p&gt;That idea appears repeatedly throughout Pudu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making failure visible
&lt;/h2&gt;

&lt;p&gt;Error handling is one of the areas where programming languages make very different philosophical choices.&lt;/p&gt;

&lt;p&gt;In Pudu, ordinary recoverable failure is intended to be represented as a value.&lt;/p&gt;

&lt;p&gt;A function that can fail can expose that directly through its signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not need to inspect the implementation or documentation to discover that &lt;code&gt;loadConfig&lt;/code&gt; may fail. The possibility is part of the function's contract.&lt;/p&gt;

&lt;p&gt;Pudu also provides &lt;code&gt;?&lt;/code&gt; for propagating failure without turning every operation into deeply nested control flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pudu.toml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not mean that Pudu somehow eliminates runtime failures. That would be an unrealistic promise for a general-purpose programming language. Instead, the objective is narrower and more useful: failures that we reasonably expect software to handle should be difficult to accidentally ignore.&lt;/p&gt;

&lt;p&gt;The same philosophy applies to absence. Rather than allowing a &lt;code&gt;null&lt;/code&gt; value to appear almost anywhere, absence can be represented intentionally through types such as &lt;code&gt;Option[T]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A value is either present or absent, and code interacting with it has enough information to reason about both possibilities.&lt;/p&gt;

&lt;p&gt;These concepts are not inventions unique to Pudu. Languages from the ML family, Haskell, Rust, Swift, and others have demonstrated variations of them for years. What interests me is how these ideas can be combined into a language that remains approachable while still providing strong foundations for larger programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expression-oriented by design
&lt;/h2&gt;

&lt;p&gt;Pudu is also expression-oriented.&lt;/p&gt;

&lt;p&gt;I have always liked languages where constructs compose naturally rather than being divided into many artificial categories of statements and expressions. If something logically produces a value, the language should generally make it possible to use that value directly.&lt;/p&gt;

&lt;p&gt;This has implications beyond syntax.&lt;/p&gt;

&lt;p&gt;Expression-oriented programming encourages data flow that can often be easier to reason about. Instead of creating temporary mutable state merely to communicate the result of a branch or computation, the result can become part of the expression itself.&lt;/p&gt;

&lt;p&gt;Pudu is influenced here by languages such as Haskell, OCaml, Rust, and other members of the functional and ML traditions, but I am deliberately avoiding the goal of cloning any particular language.&lt;/p&gt;

&lt;p&gt;Influence is useful. I don't think imitation is enough reason to create a programming language.&lt;/p&gt;

&lt;p&gt;Pudu needs to develop its own identity and its own answers to these problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  A language is more than its syntax
&lt;/h2&gt;

&lt;p&gt;One lesson that became increasingly clear while building Pudu is that designing syntax is probably the easiest part of creating a programming language.&lt;/p&gt;

&lt;p&gt;The difficult work begins when that syntax needs semantics.&lt;/p&gt;

&lt;p&gt;The parser needs to recover intelligently from incomplete programs. The type checker needs to understand what the programmer intended and explain when those expectations cannot be satisfied. Modules need predictable resolution rules. Diagnostics need stable source locations. The standard library needs coherent conventions. Editor tooling needs to understand programs while they are being written, not only after they successfully compile.&lt;/p&gt;

&lt;p&gt;Then everything has to work together.&lt;/p&gt;

&lt;p&gt;Because of that, I don't consider Pudu to be only the language grammar. I think of Pudu as the complete development environment surrounding the language.&lt;/p&gt;

&lt;p&gt;The project currently uses a single &lt;code&gt;pudu&lt;/code&gt; executable as its primary interface. That executable is responsible for operations such as checking programs, running them, testing projects, formatting source code, linting, generating documentation, bundling programs, and serving editor functionality through the Language Server Protocol.&lt;/p&gt;

&lt;p&gt;The intended experience is deliberately straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pudu init hello
&lt;span class="nb"&gt;cd &lt;/span&gt;hello
pudu run src/Main.pudu
pudu &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Editor support is exposed through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pudu lsp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A VS Code extension is also being developed alongside the language.&lt;/p&gt;

&lt;p&gt;I want the tooling to feel like part of Pudu rather than an ecosystem of unrelated programs that users have to discover and assemble themselves.&lt;/p&gt;

&lt;p&gt;Installing a programming language should get you much closer to actually programming in that language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the implementation in Haskell
&lt;/h2&gt;

&lt;p&gt;The Pudu implementation is currently being developed in Haskell.&lt;/p&gt;

&lt;p&gt;For a compiler project, Haskell provides a number of useful properties. Algebraic data types map naturally to syntax trees and intermediate representations. Pattern matching works well for compiler transformations. Strong typing makes many invalid internal states harder to construct, and functional composition fits naturally with the different phases of a compiler.&lt;/p&gt;

&lt;p&gt;But using Haskell does not mean Pudu is intended to become Haskell with different syntax.&lt;/p&gt;

&lt;p&gt;Pudu has its own design goals.&lt;/p&gt;

&lt;p&gt;The implementation language is a tool used to build those ideas, not a restriction on what the final language should become.&lt;/p&gt;

&lt;p&gt;Working on the compiler has also changed how I think about programming languages in general. Features that appear almost trivial from the outside quickly turn into deeper design questions.&lt;/p&gt;

&lt;p&gt;What happens after the parser encounters malformed syntax? How much should it recover before continuing? When two interpretations of an expression are possible, which rule wins? What information should survive from parsing into type checking? How should generic types be represented? What makes an error message genuinely useful instead of merely technically correct?&lt;/p&gt;

&lt;p&gt;Once you build a compiler, things that previously looked like language syntax start looking like contracts between multiple systems.&lt;/p&gt;

&lt;p&gt;That has probably been the most interesting part of the project for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler diagnostics matter
&lt;/h2&gt;

&lt;p&gt;One area I care particularly about is diagnostics.&lt;/p&gt;

&lt;p&gt;A compiler error is a user interface.&lt;/p&gt;

&lt;p&gt;When something is wrong, telling the programmer that the program is invalid is technically correct but practically insufficient. A useful compiler should identify where the problem originated, explain what it expected, preserve enough context to make the message understandable, and avoid producing a cascade of unrelated errors caused by the first mistake.&lt;/p&gt;

&lt;p&gt;This becomes particularly important for editor integration.&lt;/p&gt;

&lt;p&gt;Developers spend much more time working with incomplete programs than perfectly valid ones. Every few keystrokes, a source file may temporarily contain missing delimiters, unfinished expressions, incomplete generic arguments, or unresolved names.&lt;/p&gt;

&lt;p&gt;A parser and language server that only behave well when the program is already valid do not provide a good development experience.&lt;/p&gt;

&lt;p&gt;For that reason, parser recovery, diagnostics, and LSP behavior are not things I want to bolt onto Pudu after the language is "finished." They are part of the language engineering itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the name Pudu?
&lt;/h2&gt;

&lt;p&gt;The name comes from the pudu, a small deer native to South America.&lt;/p&gt;

&lt;p&gt;Programming languages often end up with highly technical names, acronyms, or names intended to communicate something about their implementation. I wanted something simpler: short, recognizable, easy to say, and capable of developing an identity of its own.&lt;/p&gt;

&lt;p&gt;I came across the pudu and liked the name.&lt;/p&gt;

&lt;p&gt;Sometimes that is enough.&lt;/p&gt;

&lt;p&gt;As the project has grown, the name has also made it possible to develop a visual identity around the language without tying that identity to a particular implementation detail that might change later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Pudu actually stands today
&lt;/h2&gt;

&lt;p&gt;I think transparency is particularly important when talking about a new programming language.&lt;/p&gt;

&lt;p&gt;Pudu is currently &lt;strong&gt;pre-release software&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first milestone is &lt;code&gt;0.1.0&lt;/code&gt;, not &lt;code&gt;1.0&lt;/code&gt;, and that distinction matters.&lt;/p&gt;

&lt;p&gt;Programs are currently interpreted. Dependencies are local directories. Platform support remains limited. Some language semantics will change. APIs will evolve. Parts of the standard library will be redesigned as real programs expose weaknesses in the current design.&lt;/p&gt;

&lt;p&gt;I don't want to hide those limitations behind ambitious language.&lt;/p&gt;

&lt;p&gt;Building a mature programming language and ecosystem takes years. Rust, Go, Swift, Kotlin, and other successful languages did not become what they are today with their first release.&lt;/p&gt;

&lt;p&gt;The purpose of Pudu 0.1 is therefore not to claim that the language is finished.&lt;/p&gt;

&lt;p&gt;It is to establish a credible foundation.&lt;/p&gt;

&lt;p&gt;I want the language, compiler, standard library, documentation, tooling, editor integration, tests, examples, and development workflow to reach a point where other developers can meaningfully experiment with Pudu and provide useful feedback.&lt;/p&gt;

&lt;p&gt;That is a much more valuable milestone than prematurely declaring stability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I want to take it
&lt;/h2&gt;

&lt;p&gt;The longer-term direction is to make Pudu capable of building serious software while keeping the language understandable.&lt;/p&gt;

&lt;p&gt;I want a language where the type system helps communicate intent without dominating every program. I want error handling to remain explicit without becoming repetitive. I want pattern matching and algebraic data types to feel natural rather than advanced. I want compiler diagnostics that developers can actually learn from.&lt;/p&gt;

&lt;p&gt;I also want tooling to remain a first-class concern as the project grows.&lt;/p&gt;

&lt;p&gt;It is easy for a language to accumulate independent tools over time until basic development requires configuring a compiler, formatter, linter, test runner, package manager, language server, documentation generator, and build system independently.&lt;/p&gt;

&lt;p&gt;Pudu has an opportunity that mature languages often don't have: these decisions can be considered together from the beginning.&lt;/p&gt;

&lt;p&gt;Performance is another important part of the long-term work. The current interpreter is appropriate for developing and validating the language, but it is not the end of the story. Moving toward serious native applications will eventually require increasingly sophisticated compilation and optimization work.&lt;/p&gt;

&lt;p&gt;I would rather approach that incrementally and measure the results than make performance claims before the implementation can support them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What building Pudu has taught me
&lt;/h2&gt;

&lt;p&gt;Building Pudu has made me appreciate how much invisible engineering exists underneath a mature programming language.&lt;/p&gt;

&lt;p&gt;As developers, we write &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, call a generic function, import a module, or ask an editor for autocomplete without thinking much about what happens underneath.&lt;/p&gt;

&lt;p&gt;When you build the language yourself, none of those behaviors simply exist.&lt;/p&gt;

&lt;p&gt;Someone has to define what a module actually means. Someone has to determine when two types are compatible. Someone has to decide whether a parser should stop or recover after an error. Someone has to define precedence, name resolution, visibility, generic substitution, diagnostics, package boundaries, source locations, formatting rules, and hundreds of other details.&lt;/p&gt;

&lt;p&gt;And once those decisions are made, they have to remain coherent with one another.&lt;/p&gt;

&lt;p&gt;That has become one of my favorite parts of this project.&lt;/p&gt;

&lt;p&gt;Pudu is not only an attempt to create another programming language. It has become a way for me to study programming languages at a much deeper level by actually building one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The road to 0.1
&lt;/h2&gt;

&lt;p&gt;Right now, my focus is getting the foundations right.&lt;/p&gt;

&lt;p&gt;I would rather spend more time fixing inconsistencies in the language, improving diagnostics, strengthening the standard library, testing edge cases, and polishing the development experience than rush toward a version number.&lt;/p&gt;

&lt;p&gt;There will be plenty of opportunities to add features later.&lt;/p&gt;

&lt;p&gt;The difficult part is deciding which foundations should remain when those features arrive.&lt;/p&gt;

&lt;p&gt;Pudu 0.1 will still be experimental. That is expected. What I want it to demonstrate is that the ideas behind the language can work together as a coherent system.&lt;/p&gt;

&lt;p&gt;From there, real programs and real developers can begin challenging those assumptions.&lt;/p&gt;

&lt;p&gt;That feedback is ultimately how the language will improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is only the beginning
&lt;/h2&gt;

&lt;p&gt;Pudu is still a small project compared with established programming languages, and there is a significant amount of engineering ahead.&lt;/p&gt;

&lt;p&gt;That's also what makes this stage exciting.&lt;/p&gt;

&lt;p&gt;The architecture is still evolving. Some decisions can still be reconsidered. The standard library is still taking shape. The compiler can still change substantially. There are still opportunities to experiment before compatibility requirements make those decisions much more expensive.&lt;/p&gt;

&lt;p&gt;I'm building Pudu in the open, and the project is available on GitHub.&lt;/p&gt;

&lt;p&gt;If you're interested in programming languages, compiler engineering, type systems, language tooling, or open-source development, you're welcome to explore the source code, try the language, open an issue, contribute, or simply follow the project as it develops.&lt;/p&gt;

&lt;p&gt;Pudu doesn't need to pretend to be the future of programming.&lt;/p&gt;

&lt;p&gt;Right now, it needs to become a good programming language.&lt;/p&gt;

&lt;p&gt;That's the challenge I'm interested in solving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pudu is currently heading toward its 0.1.0 pre-release.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/chrismichaelps/pudu-lang" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/pudu-lang&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>compiler</category>
      <category>programminglanguages</category>
    </item>
    <item>
      <title>Why I Built ACP: A Coordination Platform for Autonomous Coding Agents</title>
      <dc:creator>Chris M. Pérez</dc:creator>
      <pubDate>Sun, 05 Jul 2026 16:24:07 +0000</pubDate>
      <link>https://dev.to/chrismichaelps/why-i-built-acp-a-coordination-platform-for-autonomous-coding-agents-2ogc</link>
      <guid>https://dev.to/chrismichaelps/why-i-built-acp-a-coordination-platform-for-autonomous-coding-agents-2ogc</guid>
      <description>&lt;p&gt;As coding agents continue to improve, I've noticed that the hardest problems are no longer about generating code. Claude Code, Codex, Gemini CLI, Cursor, and other agents are already capable of implementing features, refactoring applications, writing tests, and navigating large repositories with very little guidance.&lt;/p&gt;

&lt;p&gt;The real challenge begins when multiple autonomous workers need to collaborate on the same project.&lt;/p&gt;

&lt;p&gt;Most development tooling was designed around humans. Git records history, GitHub manages collaboration, CI systems automate builds, and editors provide an interface for writing code. Those tools work extremely well because humans naturally communicate outside the system. We discuss implementation details in Slack, leave comments on pull requests, create tickets, and coordinate work through meetings.&lt;/p&gt;

&lt;p&gt;AI agents don't have those implicit communication channels.&lt;/p&gt;

&lt;p&gt;Every agent starts from its own context window. It doesn't know whether another worker is already implementing the same feature, whether a file is currently being modified, which decisions have already been made, or what work remains after another agent terminates unexpectedly. As more autonomous workers participate in software development, coordination becomes a systems problem rather than a prompting problem.&lt;/p&gt;

&lt;p&gt;That observation led me to build &lt;strong&gt;ACP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike existing agent protocols that focus on communication between clients and models or interoperability between agents, ACP focuses on something different: &lt;strong&gt;shared operational state inside a software project&lt;/strong&gt;. The goal is to provide a durable coordination layer that allows autonomous workers to safely collaborate on the same repository.&lt;/p&gt;

&lt;p&gt;Instead of treating conversations as the source of truth, ACP models the state of collaborative software engineering itself.&lt;/p&gt;

&lt;p&gt;A workspace becomes the boundary where work happens. Workers represent autonomous actors, whether they're AI agents, humans, CI pipelines, or automation. Work is represented through explicit work units with predictable lifecycle transitions instead of loosely defined tasks. Shared resources can be coordinated using advisory leases, allowing workers to understand ownership without relying on operating-system locks. Long-running execution can be resumed through checkpoints, while durable memory records preserve the information another worker actually needs to continue the job. Reviews, artifacts, and event history complete the lifecycle so every important action becomes part of the operational history of the workspace.&lt;/p&gt;

&lt;p&gt;One design decision that significantly influenced ACP is the idea that &lt;strong&gt;coordination should be event-driven&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than simply mutating database records, important operations generate immutable events describing what happened. A worker claims a work unit. A lease is acquired. Progress is checkpointed. A review is requested. An artifact is attached. Work is approved and eventually completed.&lt;/p&gt;

&lt;p&gt;This event history becomes far more valuable than a chat transcript because it allows any worker to reconstruct the current state of the workspace without needing access to the original conversation. If an agent crashes, a CI job restarts, or another model takes over the task, recovery is based on durable state rather than reconstructed prompts.&lt;/p&gt;

&lt;p&gt;Another important architectural goal was separating &lt;strong&gt;coordination semantics from infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether clients communicate through REST, JSON-RPC, Server-Sent Events, or native Effect RPC should not change the underlying behavior of the platform. Likewise, whether persistence is backed by in-memory storage during development, SQLite on a workstation, or PostgreSQL in production should not affect how work units, leases, checkpoints, or reviews behave. Infrastructure can evolve independently while the coordination model remains consistent.&lt;/p&gt;

&lt;p&gt;The implementation reflects that philosophy throughout the repository.&lt;/p&gt;

&lt;p&gt;ACP is built with TypeScript and the Effect ecosystem, using Effect's dependency graph, typed errors, schema validation, configuration system, and runtime abstractions to keep each subsystem isolated and composable. The project separates protocol definitions, domain logic, infrastructure adapters, transports, storage implementations, and application entry points into independent layers, making it possible to evolve individual components without affecting the rest of the platform.&lt;/p&gt;

&lt;p&gt;One aspect I'm particularly happy with is that ACP is not tied to a single execution environment. The same coordination model can support local repositories, Git worktrees, development containers, cloud sandboxes, or CI pipelines. Workers don't need to know where they're running—they only need to understand the shared coordination state exposed by the platform.&lt;/p&gt;

&lt;p&gt;ACP is intentionally &lt;strong&gt;not&lt;/strong&gt; another AI framework.&lt;/p&gt;

&lt;p&gt;It doesn't generate code.&lt;/p&gt;

&lt;p&gt;It doesn't orchestrate prompts.&lt;/p&gt;

&lt;p&gt;It doesn't replace Git.&lt;/p&gt;

&lt;p&gt;It doesn't compete with MCP or other agent communication protocols.&lt;/p&gt;

&lt;p&gt;Instead, ACP sits one layer below autonomous software engineering, providing the durable operational primitives that allow independent workers to collaborate safely over long-running tasks.&lt;/p&gt;

&lt;p&gt;Building ACP has reinforced something I've been thinking about for a while.&lt;/p&gt;

&lt;p&gt;The first generation of AI developer tools focused on making individual developers more productive. The next generation will focus on enabling entire teams of autonomous workers to collaborate effectively. Once that happens, the difficult engineering problems won't be language models—they'll be ownership, recovery, synchronization, review workflows, auditability, and distributed coordination.&lt;/p&gt;

&lt;p&gt;That's the problem ACP is exploring.&lt;/p&gt;

&lt;p&gt;The project is still evolving, and many ideas will undoubtedly change over time. But I believe the underlying premise is sound: autonomous software engineering needs the same kind of operational infrastructure that distributed systems required years ago. As agents become more capable, coordination becomes infrastructure—not an implementation detail.&lt;/p&gt;

&lt;p&gt;If you'd like to explore the architecture or contribute ideas, the repository is open source:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/chrismichaelps/acp" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/acp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Beyond Prompt Engineering: Why Your AI Architecture Is Leaking Tokens (And How to Fix It with FMCF)</title>
      <dc:creator>Chris M. Pérez</dc:creator>
      <pubDate>Fri, 27 Feb 2026 22:33:54 +0000</pubDate>
      <link>https://dev.to/chrismichaelps/beyond-prompt-engineering-why-your-ai-architecture-is-leaking-tokens-and-how-to-fix-it-with-fmcf-5kb</link>
      <guid>https://dev.to/chrismichaelps/beyond-prompt-engineering-why-your-ai-architecture-is-leaking-tokens-and-how-to-fix-it-with-fmcf-5kb</guid>
      <description>&lt;p&gt;We have all hit the "stochastic wall." You start a development project with a top-tier AI model—whether it is &lt;strong&gt;GPT-4o&lt;/strong&gt;, &lt;strong&gt;Claude 3.5&lt;/strong&gt;, or a specialized local LLM—and for the first twenty minutes, the speed is incredible. However, as the codebase grows and the conversation history deepens, a subtle but destructive breakdown begins to set in.&lt;/p&gt;

&lt;p&gt;Regardless of your experience level, the symptoms are the same: &lt;strong&gt;Context Smog&lt;/strong&gt;, &lt;strong&gt;Architectural Drift&lt;/strong&gt;, and the dreaded &lt;strong&gt;Hallucination Loop&lt;/strong&gt;. The model starts to "forget" logic established just a few turns ago, or worse, it starts making up rules that violate your project’s core DNA.&lt;/p&gt;

&lt;p&gt;Having a background in traditional coding means I value precision, but I found myself manually correcting AI outputs too often. I needed a system where the AI worked as a reliable partner rather than an unpredictable assistant. This led to the development of &lt;strong&gt;FMCF (Fibonacci Matrix Context Flow)&lt;/strong&gt;. This is not just a clever prompt; it is a universal architectural rulebook that transforms the AI into a high-precision machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Core Constraint: Second-Order Markov Determinism
&lt;/h2&gt;

&lt;p&gt;The main challenge with AI models is their random nature; they predict the next word based on probability rather than solid logic. When too much information piles up in the "chat history," the model struggles to focus on what actually matters.&lt;/p&gt;

&lt;p&gt;FMCF fixes this by enforcing &lt;strong&gt;Second-Order Markov Determinism&lt;/strong&gt;. In this setup, the next step ($V_{n+1}$) is a strict result of only the current state ($V_n$) and the one right before it ($V_{n-1}$). By explicitly declaring everything outside this two-step window as &lt;strong&gt;Null-Space&lt;/strong&gt;, we clear out the "extra noise" and "zombie logic" that usually leads to AI mistakes. This ensures the model stays grounded in the immediate architectural context.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Dual-Track Registry: Planning Before Building
&lt;/h2&gt;

&lt;p&gt;FMCF uses a &lt;strong&gt;Hash-First Hard-Lock&lt;/strong&gt; to make sure that the actual building never gets ahead of the design. We split the work into two distinct areas, or "planes":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Track 1: The Implementation Plane (The Shadow):&lt;/strong&gt; This is where the actual code lives. Only small, specific changes (Targeted Line Injections) are allowed here to prevent unexpected side effects across the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track 2: The Hash Registry Plane (The Source):&lt;/strong&gt; This is the system's "truth" layer. Before a single line of code is written, the AI must update records like &lt;code&gt;.contract.json&lt;/code&gt; (input/output rules), &lt;code&gt;.logic.md&lt;/code&gt; (step-by-step plans), and &lt;code&gt;.chronos.json&lt;/code&gt; (the history of "why" a change was made).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;local.map.json&lt;/code&gt; / &lt;code&gt;.index.json&lt;/code&gt; (Dynamic Topology)
&lt;/h3&gt;

&lt;p&gt;To manage these tracks, FMCF uses a topology schema to track every module's state and dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shard_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@root/src/module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state_anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BigInt:0x..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parent_bridge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@root/hashes/local.map.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"git_anchor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HEAD_SHA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VERIFIED | STALE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@root/src/module/file.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hash_reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@root/hashes/module/file.hash.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"grammar_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@root/hashes/grammar/[lang].hash.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"@root/hashes/dep.contract.json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fidelity_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Active | Signature | Hash"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI is strictly forbidden from writing code until these registry files are updated and verified. This creates a deterministic link between intent and execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Cache Trust Protocol: Eliminating Stale State
&lt;/h2&gt;

&lt;p&gt;LLM sessions are volatile and "forgetful." When you start a new session or upload files, how do you know the AI actually "understands" the current state of the project? FMCF solves this with the &lt;strong&gt;Cache Trust Protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before a single line of logic is processed, the AI must perform an &lt;strong&gt;Integrity Handshake&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sample:&lt;/strong&gt; Pick 3 random entries from the &lt;code&gt;/hashes/&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate:&lt;/strong&gt; Re-compute the hashes of the source files and compare them to the registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verdict:&lt;/strong&gt; If they match, the cache is &lt;code&gt;VERIFIED&lt;/code&gt;. If not, it is &lt;code&gt;STALE&lt;/code&gt;, and a full re-scan is mandatory.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Fixing Errors: Active Grammar Shards
&lt;/h2&gt;

&lt;p&gt;A major frustration in AI development happens when a model writes code that immediately causes errors because it relied on its general training data instead of your project’s specific needs.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;FMCF&lt;/strong&gt;, I added &lt;strong&gt;Step -0.5: Signature Discovery&lt;/strong&gt;. Before writing code, the AI must scan your environment—specifically your &lt;code&gt;package.json&lt;/code&gt; or setup files—to lock its "grammar" to the versions you actually use.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;grammar/[lang].hash.md&lt;/code&gt; (Linguistic DNA)
&lt;/h3&gt;

&lt;p&gt;This shard acts as the "Hard Compiler Constraint" for the AI's syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;Language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;e.g.&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;TypeScript | Rust | Solidity&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;e.g.&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;5.x | 1.75 | 0.8.20&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;Fidelity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100% (Static Reference)&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gu"&gt;## [Syntax_Rules]&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; (e.g., Strict Null Checks)
&lt;span class="p"&gt;-&lt;/span&gt; (e.g., Functional composition over classes)

&lt;span class="gu"&gt;## [Naming_Conventions]&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; (e.g., camelCase for variables, PascalCase for components)

&lt;span class="gu"&gt;## [Import_Order]&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; (e.g., External libraries, Internal modules, Types)

&lt;span class="gu"&gt;## [Prohibited_Patterns]&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; (e.g., No explicit 'any', No 'var')

&lt;span class="gu"&gt;## [Standard_Library_Signatures]&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; (Immutable reference to core methods to avoid tokenized re-learning)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By anchoring the AI's "Grammar Handshake" to these specific rules, we prevent repetitive syntax errors and save thousands of tokens.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. The Forensic Audit: Keeping the AI Honest
&lt;/h2&gt;

&lt;p&gt;To keep the system fast and efficient, the prompt includes a dedicated &lt;strong&gt;Forensic Audit&lt;/strong&gt; layer. This role acts as a &lt;strong&gt;Treasurer&lt;/strong&gt;, checking the session for wasted space and "leaking" tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;.chronos.json&lt;/code&gt; (Forensic Ledger)
&lt;/h3&gt;

&lt;p&gt;Every change is logged here to maintain a clear audit trail of intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BigInt:0x..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"logic_delta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Brief 'Why'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"High|Med|Low"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commit_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SHA_7"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Treasurer audits the session, verifying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context Cleanup:&lt;/strong&gt; Making sure old, unneeded information (Context Smog) has been cleared out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role Integrity:&lt;/strong&gt; Confirming that specific specialists (like the Architect) didn't try to write implementation code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability:&lt;/strong&gt; Ensuring every change has a clear explanation of "Why" it was made in the &lt;code&gt;logic_delta&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the session gets too cluttered or the &lt;strong&gt;Token Efficiency Score&lt;/strong&gt; drops below the Golden Ratio ($61.8\%$), the system triggers a hard reset (World State Vector) to start fresh.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. The Power of Model Sinergy
&lt;/h2&gt;

&lt;p&gt;FMCF doesn't just help powerful models; it levels the playing field for smaller models too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For High-Tier Models:&lt;/strong&gt; It acts as a set of guardrails, preventing the model from over-complicating logic or drifting from the architectural plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Small/Local Models:&lt;/strong&gt; The fragmented "sharded" nature of the metadata allows these models to process specific contracts without needing to hold the entire project context in their limited memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using the &lt;strong&gt;Hash-First Hard-Lock&lt;/strong&gt;, even a smaller model can perform complex updates reliably because the "logic" is pre-defined in Track 2 before it ever attempts to touch the code in Track 1.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters for All Developers
&lt;/h2&gt;

&lt;p&gt;Whether you are a junior dev just starting out or a seasoned pro, you do not need an AI that "guesses"; you need a partner that follows your rules exactly. FMCF makes the AI's logic clear, tracked, and predictable. It turns AI coding from a gamble into a forensic process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Hash is the Truth. The Grammar is the Law. The History is the Evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Explore the full repo and get the Master Seeds for your own projects here:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/chrismichaelps/FMCF" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/FMCF&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tagix: My Experimental State Management Library</title>
      <dc:creator>Chris M. Pérez</dc:creator>
      <pubDate>Sun, 08 Feb 2026 16:38:49 +0000</pubDate>
      <link>https://dev.to/chrismichaelps/tagix-my-experimental-state-management-library-3e5e</link>
      <guid>https://dev.to/chrismichaelps/tagix-my-experimental-state-management-library-3e5e</guid>
      <description>&lt;h2&gt;
  
  
  Tagix: My Experimental State Management Library
&lt;/h2&gt;

&lt;p&gt;Every time I write a state handler, I find myself asking the same question: &lt;strong&gt;What happens if I miss a case?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;State management is about transitions. Your application moves from one state to another, and your code needs to handle every possibility. But humans miss things. We forget edge cases. We assume certain states won't happen.&lt;/p&gt;

&lt;p&gt;What if TypeScript could help? What if, instead of relying on tests to catch missed handlers, we could make TypeScript do that work for us?&lt;/p&gt;

&lt;p&gt;That's the question that started Tagix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem I Was Trying to Solve
&lt;/h2&gt;

&lt;p&gt;I wanted to write state transitions where missing a case would be a type error, not a runtime bug.&lt;/p&gt;

&lt;p&gt;Consider this state that can be in one of four conditions:&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;UserState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authenticated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;message&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most state management libraries, you'd write a reducer that handles these cases. If you forget one, the application might crash when that unexpected state occurs. Your tests might catch it, or they might not.&lt;/p&gt;

&lt;p&gt;But TypeScript's discriminated unions give us something powerful: the ability to narrow types based on runtime values. When you check &lt;code&gt;state.status === 'authenticated'&lt;/code&gt;, TypeScript knows inside that branch that &lt;code&gt;state.user&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;I wanted to build a state management library that leveraged this—not as an optional feature, but as the default way of working with state.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Core Insight
&lt;/h2&gt;

&lt;p&gt;The insight was simple: &lt;strong&gt;state should be defined as tagged unions, and state transitions should be functions that return one of those tagged variants.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No action dispatching in the traditional sense. No action types as strings. No middleware pipeline. Just state, transitions, and TypeScript making sure you didn't miss anything.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in Tagix:&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;const&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this definition, Tagix generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A discriminated union type for the state&lt;/li&gt;
&lt;li&gt;Constructor functions for each variant&lt;/li&gt;
&lt;li&gt;Type guards for runtime checks&lt;/li&gt;
&lt;li&gt;A pattern matcher for declarative handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you create an action, you're creating a function that takes the current state and returns a new state:&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;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&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="nl"&gt;password&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&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="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&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;password&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;attemptLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&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;The key point isn't the API—it's that this function &lt;strong&gt;must&lt;/strong&gt; return a valid &lt;code&gt;UserState&lt;/code&gt;. TypeScript won't let you return anything else. If you try to return an invalid state, you'll know immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Pattern Matching Matters
&lt;/h2&gt;

&lt;p&gt;I spent a lot of time thinking about how developers would read state in their components. Most libraries use selectors or subscriptions. These work, but they don't leverage TypeScript's type narrowing.&lt;/p&gt;

&lt;p&gt;In Tagix, you use the &lt;code&gt;$match&lt;/code&gt; function:&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;function&lt;/span&gt; &lt;span class="nf"&gt;displayUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserState&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Waiting to start...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading your account...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`Welcome back, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`Something went wrong: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;This function handles &lt;strong&gt;every&lt;/strong&gt; state variant. Add a new variant to &lt;code&gt;UserState&lt;/code&gt;, and TypeScript forces you to add a handler here. No default case to forget, no implicit fallthrough.&lt;/p&gt;

&lt;p&gt;The benefit is confidence. When your code type-checks, you've handled every possible state.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Abstractions
&lt;/h2&gt;

&lt;p&gt;Tagix is built around three fundamental abstractions that work together. Understanding these will help you use the library effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tagged Enum
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;taggedEnum&lt;/code&gt; function creates a state constructor and a set of type utilities:&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;const&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line does a lot of work. First, it creates a type &lt;code&gt;UserStateType&lt;/code&gt; that is the union of all variants. Second, it creates constructor functions for each variant (&lt;code&gt;UserState.Idle()&lt;/code&gt;, &lt;code&gt;UserState.Loading()&lt;/code&gt;, etc.). Third, it creates a &lt;code&gt;$match&lt;/code&gt; function for pattern matching. Fourth, it creates &lt;code&gt;$is&lt;/code&gt; type guards for each variant.&lt;/p&gt;

&lt;p&gt;The constructors are particularly useful because they create both the runtime value and the type simultaneously:&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;const&lt;/span&gt; &lt;span class="nx"&gt;authenticatedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&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="c1"&gt;// authenticatedState has type: { _tag: 'Authenticated'; user: User }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;Actions in Tagix are different from what you might have used elsewhere. An action is a function that takes the current state and returns a new state:&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;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&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="nl"&gt;password&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user/login&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="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&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;password&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;attemptLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&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;The key insight is that actions return state directly. There's no dispatching to a reducer, no action types to match, no switch statements. Your action is your state transition.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.withPayload()&lt;/code&gt; and &lt;code&gt;.withState()&lt;/code&gt; calls set up type inference. The &lt;code&gt;.withPayload&amp;lt;PayloadType&amp;gt;()&lt;/code&gt; tells Tagix what shape the payload has. The &lt;code&gt;.withState()&lt;/code&gt; tells Tagix what state this action works with.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Store
&lt;/h3&gt;

&lt;p&gt;The store ties everything together:&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;const&lt;/span&gt; &lt;span class="nx"&gt;userStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;userStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;userStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The store holds the current state and handles action dispatching. When you register an action, the store knows which actions are available. When you dispatch an action, the store calls it with the current state.&lt;/p&gt;

&lt;p&gt;You can subscribe to state changes:&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="nx"&gt;userStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;State changed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&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;h2&gt;
  
  
  Async Actions
&lt;/h2&gt;

&lt;p&gt;Real-world applications have async operations. Tagix handles this naturally because action functions can be async:&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;const&lt;/span&gt; &lt;span class="nx"&gt;loadUserData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user/load-data&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="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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;userId&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="k"&gt;try&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;userId&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;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The store handles awaiting automatically. While the promise resolves, the state is in whatever variant you returned first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing State Machines
&lt;/h2&gt;

&lt;p&gt;One of the benefits of Tagix is that it encourages thinking about your state as a proper state machine. Every state is a variant, every transition is explicit.&lt;/p&gt;

&lt;p&gt;Here's how I'd design an authentication flow:&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;const&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Unauthenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Authenticating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&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="na"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;sessionExpiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;permissions&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;SessionExpired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;LoggingOut&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&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="nl"&gt;password&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;auth/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&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;password&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifyCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sessionExpiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permissions&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Unauthenticated&lt;/span&gt;&lt;span class="p"&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;logout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;auth/logout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&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="nf"&gt;clearSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Unauthenticated&lt;/span&gt;&lt;span class="p"&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;checkSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;auth/check-session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&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;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifySession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionExpiresAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SessionExpired&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentState&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;Notice how every action has a clear entry and exit point. You can look at any action and understand what states it can produce. You can look at any state and understand how you got there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling Patterns
&lt;/h2&gt;

&lt;p&gt;Tagix works well with error boundaries and recovery flows:&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;const&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Initial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ErrorInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;retryCount&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;data/load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;&amp;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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&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;retryCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_ERROR&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;retryCount&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;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resetError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;data/reset-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DataState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Initial&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;The error state includes a &lt;code&gt;retryCount&lt;/code&gt;, allowing you to implement backoff strategies or give up after a certain number of attempts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining State Machines
&lt;/h2&gt;

&lt;p&gt;Real applications often have multiple state machines that interact. Here are approaches that work:&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel State Machines
&lt;/h3&gt;

&lt;p&gt;If you have independent state machines (like a user session and a modal dialog), you can combine them:&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;const&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&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;ModalState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&gt;/* ... */&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;AppState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ModalState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&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;appStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModalState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Closed&lt;/span&gt;&lt;span class="p"&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;openSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app/open-settings&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="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;currentState&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModalState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Settings&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;h3&gt;
  
  
  Nested State Machines
&lt;/h3&gt;

&lt;p&gt;For dependent state machines, you can nest one inside another:&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;const&lt;/span&gt; &lt;span class="nx"&gt;CheckoutState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Shipping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shippingInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShippingInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;shippingMethod&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="na"&gt;Payment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShippingInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentMethod&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;orderId&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="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Shopping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;CheckoutState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Browsing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&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;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkoutWithCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Shopping&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CheckoutState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cart&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;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Tagix is designed to be minimal in runtime overhead. Here are some patterns for keeping things fast:&lt;/p&gt;

&lt;h3&gt;
  
  
  Selective Subscriptions
&lt;/h3&gt;

&lt;p&gt;You don't always need to subscribe to the entire state:&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="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authenticated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;updateUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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;h3&gt;
  
  
  Memoization with Selectors
&lt;/h3&gt;

&lt;p&gt;For derived data, use memoization:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;memoize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tagix&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;selectUserDisplayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not logged in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error&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="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;selectUserDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Patterns
&lt;/h2&gt;

&lt;p&gt;Here are patterns that emerge naturally when using Tagix:&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization Actions
&lt;/h3&gt;

&lt;p&gt;Many applications have an initialization phase:&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;const&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Initializing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;progress&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="na"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppConfig&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&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="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;app/initialize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&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;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadPreferences&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;config&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;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;preferences&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;h3&gt;
  
  
  Optimistic Updates
&lt;/h3&gt;

&lt;p&gt;For better user experience, you can update the UI optimistically:&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;const&lt;/span&gt; &lt;span class="nx"&gt;toggleTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos/toggle&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="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&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;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;TodoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentState&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;h3&gt;
  
  
  Undo/Redo
&lt;/h3&gt;

&lt;p&gt;Implementing undo/redo is straightforward with Tagix:&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;const&lt;/span&gt; &lt;span class="nx"&gt;EditableState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;taggedEnum&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;UndoStack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;RedoStack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;undo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;EditableState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;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;editor/undo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withPayload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;currentState&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EditableState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Current&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EditableState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UndoStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;EditableState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentState&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;h2&gt;
  
  
  Testing Strategies
&lt;/h2&gt;

&lt;p&gt;Tagix's explicit state transitions make testing straightforward:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login action&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions to Authenticated on success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Idle&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;mockPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loginAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;attemptLogin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &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="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test&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="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authenticated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authenticated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test&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="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transitions to Error on failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Idle&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;mockPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrong&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loginAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;attemptLogin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &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="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid credentials&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;The explicit transitions mean you can test each possible path through your state machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Honest Note About Scope
&lt;/h2&gt;

&lt;p&gt;Tagix isn't trying to compete with established libraries. Redux has a decade of ecosystem, DevTools, and community patterns. Zustand is minimal and works incredibly well for most use cases. MobX has influenced how we think about reactivity.&lt;/p&gt;

&lt;p&gt;Tagix is different—an experiment in taking TypeScript's type system seriously for state management. It makes certain trade-offs that larger libraries wouldn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your state must be a discriminated union&lt;/li&gt;
&lt;li&gt;You need to think about state transitions upfront&lt;/li&gt;
&lt;li&gt;The ecosystem is newer and smaller&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already happy with your current state management solution, Tagix isn't trying to convince you to switch. But if you've wished for stronger type guarantees, or experienced runtime bugs from missing state handlers, Tagix might be worth exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Design Decisions
&lt;/h2&gt;

&lt;p&gt;Building Tagix forced me to make choices. Here are the important ones:&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 1: No Action Types as Strings
&lt;/h3&gt;

&lt;p&gt;Other libraries use strings like &lt;code&gt;'USER_LOGIN_REQUEST'&lt;/code&gt; to identify actions. In Tagix, actions are functions. When you dispatch &lt;code&gt;login&lt;/code&gt;, you're dispatching the &lt;code&gt;login&lt;/code&gt; function. No string comparison happens.&lt;/p&gt;

&lt;p&gt;You can't accidentally dispatch the wrong action type. No typos in action constants. The function is the action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 2: Actions Return State Directly
&lt;/h3&gt;

&lt;p&gt;In Redux, actions are dispatched and reducers handle them. In Tagix, actions &lt;strong&gt;are&lt;/strong&gt; the state transition logic. The action function receives the current state and returns the new state.&lt;/p&gt;

&lt;p&gt;The flow is obvious. No middleware intercepting actions, no mysterious state updates in reducers you didn't write. Your action is your state transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 3: Exhaustive Pattern Matching
&lt;/h3&gt;

&lt;p&gt;Every time you use &lt;code&gt;$match&lt;/code&gt;, you're forced to handle every variant. No partial handlers, no forgotten cases.&lt;/p&gt;

&lt;p&gt;This isn't always convenient—there are times when you genuinely don't care about certain states—but it's consistent. You always know what states you're handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building Tagix taught me several things about state management and type systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, design matters more than implementation.&lt;/strong&gt; The way you model your state affects everything downstream. Tagged unions encourage thinking about state as distinct modes rather than collections of properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, type systems can do more than we typically ask.&lt;/strong&gt; We're used to TypeScript catching type mismatches. But it can also enforce behavioral invariants—transitions, exhaustiveness, state machine validity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, simplicity beats features.&lt;/strong&gt; Tagix has fewer features than Redux or other libraries. But the features it has are well-integrated. Every abstraction builds on the previous ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth, documentation matters.&lt;/strong&gt; Building a library is only half the work. Explaining how to use it, showing patterns, providing examples—that's where the real challenge lies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Tagix Fits
&lt;/h2&gt;

&lt;p&gt;Tagix is an experiment. It works best when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application has clear state modes (loading/success/error, authenticated/unauthenticated)&lt;/li&gt;
&lt;li&gt;You want TypeScript to catch bugs at the type-checking stage&lt;/li&gt;
&lt;li&gt;You're willing to think about state transitions explicitly&lt;/li&gt;
&lt;li&gt;Your team appreciates type safety&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tagix isn't right for every project. If you have simple state (toggle a boolean, store a single object), a simpler solution might be better. If you're working with legacy codebases, migrating to a tagged union model might be costly.&lt;/p&gt;

&lt;p&gt;But for new applications where state complexity is high and correctness matters, Tagix offers a different way of thinking about state management.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Road Ahead
&lt;/h2&gt;

&lt;p&gt;Tagix is still young. Here's what's coming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better React integration with hooks&lt;/li&gt;
&lt;li&gt;A DevTools extension for debugging&lt;/li&gt;
&lt;li&gt;Middleware plugins for common patterns&lt;/li&gt;
&lt;li&gt;More examples and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core idea won't change. State as tagged unions. Actions as transition functions. TypeScript as your partner in correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Effuse
&lt;/h2&gt;

&lt;p&gt;The Tagix documentation website itself is built with &lt;a href="https://github.com/chrismichaelps/effuse" rel="noopener noreferrer"&gt;Effuse&lt;/a&gt;—another library I created. Effuse is a component-based UI library with fine-grained reactivity, no virtual DOM, and the same focus on type inference that Tagix has.&lt;/p&gt;

&lt;p&gt;I built Effuse because I wanted the Tagix documentation to demonstrate the library in action. Using two experimental libraries together has been instructive—both in showing what's possible and in identifying pain points that needed addressing.&lt;/p&gt;

&lt;p&gt;If you're interested in component-based UI development with a different philosophy than React or Vue, check out Effuse as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying Tagix
&lt;/h2&gt;

&lt;p&gt;The documentation is at &lt;a href="https://tagix-docs.vercel.app/" rel="noopener noreferrer"&gt;tagix-docs.vercel.app&lt;/a&gt;. The source code is on GitHub at &lt;a href="https://github.com/chrismichaelps/tagix" rel="noopener noreferrer"&gt;github.com/chrismichaelps/tagix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's a getting started guide, API documentation, and examples. I hope you'll give it a try and let me know what you think.&lt;/p&gt;

&lt;p&gt;State management is hard. Tagix won't make it easy, but it might make it safer. And that's worth something.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source code: &lt;a href="https://github.com/chrismichaelps/tagix" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/tagix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://tagix-docs.vercel.app/" rel="noopener noreferrer"&gt;https://tagix-docs.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Effuse (library used to build the docs): &lt;a href="https://github.com/chrismichaelps/effuse" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/effuse&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Introducing Effuse — an experimental reactive framework</title>
      <dc:creator>Chris M. Pérez</dc:creator>
      <pubDate>Mon, 12 Jan 2026 23:27:20 +0000</pubDate>
      <link>https://dev.to/chrismichaelps/introducing-effuse-an-experimental-reactive-framework-4p7d</link>
      <guid>https://dev.to/chrismichaelps/introducing-effuse-an-experimental-reactive-framework-4p7d</guid>
      <description>&lt;p&gt;I built Effuse as a lightweight, reactive UI library designed for building modern web interfaces with a focus on simplicity and composability. My goal was to create a framework that prioritizes fine-grained reactivity through Signals, providing a familiar developer experience without the inherent complexity of a Virtual DOM.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@effuse/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Effuse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Powerful Internal Engine
&lt;/h2&gt;

&lt;p&gt;One of the most important technical choices I made was to power Effuse internally with the Effect library. I use Effect to make side effects and asynchronous flows more predictable and expressive.&lt;/p&gt;

&lt;p&gt;However, I want to emphasize that Effect is used strictly under the hood. I have designed the public API to be 100% Vanilla TypeScript, so you do not need to learn Effect or manage its complex return types to use Effuse. You get all the stability and performance of a functional engine while writing standard, clean TypeScript code.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Comprehensive Ecosystem
&lt;/h2&gt;

&lt;p&gt;I have expanded Effuse into a suite of specialized packages to handle different aspects of modern web development:&lt;/p&gt;

&lt;p&gt;• @effuse/core: My core reactivity engine and DOM rendering system.&lt;br&gt;
• @effuse/compiler: An optimized JSX/TSX transformer I developed to handle reactive signal access automatically, reducing boilerplate.&lt;br&gt;
• @effuse/router: A simple, type-safe router for SPA navigation.&lt;br&gt;
• @effuse/store: A functional state management library based on Effect.&lt;br&gt;
• @effuse/query: A library for reactive data fetching with built-in reliability.&lt;br&gt;
• @effuse/ink: A reactive Markdown renderer with support for embedded components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Status &amp;amp; Vision
&lt;/h2&gt;

&lt;p&gt;I want to be clear that Effuse is currently an experimental project driven by my own learning and exploration. It is not intended to compete with or replace established frameworks like React, Vue, or Solid. Instead, it is my personal space to experiment with new APIs and reactive patterns.&lt;/p&gt;

&lt;p&gt;Because the project is still in development, it is not yet ready for production use. I am primarily looking for thoughts and feedback from developers who enjoy poking around experimental projects and exploring different approaches to UI development.&lt;/p&gt;

&lt;p&gt;If you find my work interesting, please consider supporting the project with a ⭐ on GitHub!&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/chrismichaelps/effuse" rel="noopener noreferrer"&gt;https://github.com/chrismichaelps/effuse&lt;/a&gt; &lt;br&gt;
Docs: &lt;a href="https://effuse-doc.vercel.app/" rel="noopener noreferrer"&gt;https://effuse-doc.vercel.app/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
